MobDev.SecurityScan.BundledRuntime.AdvisoryFeed (mob_dev v0.7.30)

Copy Markdown View Source

Fetches and caches the upstream advisory sources the bundled-runtime layer matches against (see MobDev.SecurityScan.BundledRuntime.Advisories).

The scan has to complete offline and must not hammer the unauthenticated GitHub API (60 requests/hour), so every source goes through a file cache at advisories/ in the runtime cache ($MOB_CACHE_DIR, default ~/.mob/cache):

  1. a cached copy younger than the TTL (default 24 h) is used as-is;
  2. otherwise the source is fetched, validated, and written to the cache;
  3. if the fetch fails, an older cached copy is used and reported as stale;
  4. with no cached copy either, load/2 returns {:error, reason} and the layer falls back to manual-check notes.

A fetched body is validated before it replaces the cache, so a rate-limit page or a redesigned site can't overwrite good data with nothing.

Options

  • :advisory_cache_dir — cache directory (default default_cache_dir/0)
  • :advisory_ttl — seconds a cached copy counts as fresh (default 86_400)
  • :advisory_http_get — (url, headers) -> {:ok, status, headers, body} | {:error, reason}; defaults to :httpc. Tests inject a stub.
  • :github_token — token for the GitHub API; defaults to GITHUB_TOKEN, GH_TOKEN, then gh auth token. Unauthenticated access works.
  • :now — DateTime used for cache age (default DateTime.utc_now/0)

Summary

Types

Where the data came from. A fetch whose cache write failed carries the write error, since the next offline run will have nothing to fall back to.

Functions

Default cache directory for advisory data: advisories/ in the runtime cache.

Load one source's decoded data: [advisory] for :otp, the parsed page entries for {:openssl, branch} and :sqlite, [cycle] for :openssl_eol.

Extract the rel="next" URL from a GitHub Link header, if any.

Types

http_response()

@type http_response() ::
  {:ok, non_neg_integer(), [{String.t(), String.t()}], binary()}

provenance()

@type provenance() ::
  {:fetched, DateTime.t()}
  | {:fetched, DateTime.t(), {:cache_write_failed, String.t()}}
  | {:cached, DateTime.t()}
  | {:stale, DateTime.t(), String.t()}

Where the data came from. A fetch whose cache write failed carries the write error, since the next offline run will have nothing to fall back to.

source()

@type source() :: :otp | {:openssl, String.t()} | :openssl_eol | :sqlite

Functions

default_cache_dir()

@spec default_cache_dir() :: Path.t()

Default cache directory for advisory data: advisories/ in the runtime cache.

load(source, opts)

@spec load(
  source(),
  keyword()
) :: {:ok, term(), provenance()} | {:error, String.t()}

Load one source's decoded data: [advisory] for :otp, the parsed page entries for {:openssl, branch} and :sqlite, [cycle] for :openssl_eol.

next_link(headers)

@spec next_link([{String.t(), String.t()}]) :: String.t() | nil

Extract the rel="next" URL from a GitHub Link header, if any.