Open Source Intelligence Analysis
Open Source Intelligence Analysis adds dependency and package-intelligence context to repository reports. It is
rolling out in stages, so reports created before it is enabled show not_scanned rather than an
assumed clean result.
What the report shows
When package analysis is available, the report distinguishes:
- Confirmed curated malware matches from known vulnerability records.
- Complete, partial, unavailable, not-applicable, and not-scanned inventory states.
- Whether package intelligence came from a live, cached, stale, partial, unavailable, or not-applicable source.
- Whether every configured check applicable to the submitted source completed.
- Deterministic verdict reason codes and bounded static-finding evidence when either is present.
- A
not_applicableverdict when the artifact has no package coordinates, advisory issues, or static package findings. - Dependency paths and affected package versions when those details are available.
OSV and GitHub advisory response bodies are streamed and bounded. Query-batch entries that contain only an advisory identifier are hydrated through the bounded advisory endpoint before normalization; repeated identifiers reuse the same response, and a failed hydration is shown as partial coverage rather than silently treated as a clean result. Live provider refreshes also have an overall time budget.
Public lockfile discovery uses the same configured file, lockfile, and byte bounds as inventory
coverage. Evidence beyond a reached bound is not trusted, and only the supported version 1
uv.lock schema can provide complete PyPI coordinate evidence.
An absence of known records is not a guarantee that a package is safe.
Package-security issue tables show at most the first 20 valid advisory issues. When more valid issues are available, the report displays a note that the table is partial.
Static package findings
The scanner applies non-executing rules to package metadata and source text. It also follows bounded
literal local imports from npm lifecycle, main, and bin entry points to identify reachable
environment-to-network and network-to-process behavior. These checks can highlight install hooks,
remote shell pipelines, dynamic code construction, and environment data near outbound requests.
Static findings are reported in shadow mode by default and do not change a repository grade unless
static enforcement is explicitly enabled. The scanner also decodes only bounded, statically
recognizable Base64 and hexadecimal string literals; it never evaluates package code. Other
transform types, including array reconstruction, XOR, and constant propagation, are outside the
current supported scope rather than an execution failure. Deobfuscation is complete when every
supported bounded transform was examined. Graph limits, unresolved imports, oversized relevant
files, unreadable files, and decoding limits are reported as partial coverage. A complete static
result means the configured supported checks finished; it is never a guarantee that all runtime
behavior was understood.
Generic executable-code signatures exclude documentation and plain-text files such as Markdown,
reStructuredText, AsciiDoc, and .txt. Security guidance and intentionally unsafe examples in those
files still receive normal file analysis but are not treated as executable package behavior.
Documentation also does not consume the bounded executable static-scan file budget or count as
skipped static coverage.
Confirmed malware matches
An exact match against a curated active malware record is deterministic. The platform skips evaluative AI analysis for the affected scan and may generate one optional narrative impact summary. The executive summary always retains the malware result, F grade, zero score, unsafe decision, and incident-response containment warning. The optional model receives bounded package issues and static findings so it can explain what use at a company may mean and recommend evidence-based next steps. Only its description and recommendation are used; it cannot override the impact warning, malware result, score, safety decision, or enforcement status. If the narrative call fails or returns an inconsistent verdict, deterministic fallback guidance is shown instead.
Successful narrative metadata is retained internally for safe refresh validation but is not shown in reports. An AI summary status and reference ID appear only when summary generation fails, so the reference can be used to locate the related worker log. Known-malware reports also replace routine administrative controls with explicit do-not-deploy and incident-response actions because no administrative control makes installation or continued use of confirmed malware acceptable.
Intelligence source attribution
Every rendered package-security section identifies only the external advisory sources that were actually applicable to that scan, including a consulted source that returned no matches. Issue IDs link to canonical OSV or GitHub advisory pages. A source that was not queried and supplied no matching record is omitted instead of receiving blanket attribution.
A GHSA identifier returned through OSV is attributed to OSV because that was the consulted source. GitHub Advisory Database attribution appears when RepoRisk directly queries that provider (currently for applicable npm coordinates), even when the direct query returns no matching advisory.
GitHub Advisory Database content is used under CC BY 4.0. OSV.dev aggregates records from multiple upstream databases under source-specific licenses, so reports link to the OSV data-source and license list instead of assigning one license to all OSV records. RepoRisk retains normalized facts and canonical record links, not provider advisory prose or arbitrary reference URLs. RepoRisk analyzes the source records; neither OSV nor GitHub endorses a RepoRisk report.
Privacy and package metadata
The scanner inventories package metadata without installing, building, or running the target software. Private, unknown, and unresolved package coordinates stay local. Only coordinates already classified as public and exact are eligible for live advisory lookups. Public classification uses deterministic evidence from supported lockfiles, such as a package artifact URL on the npm, PyPI, or crates.io public registry. Operators can also configure known private and public package prefixes; private prefixes always take precedence. AI is not used to decide whether a coordinate may leave the worker.
The online_public mode queries OSV and GitHub only with positively public coordinates. Its provider
response cache is shared because the query contains public package data. OSV-Scanner is used only as
a non-executing inventory extractor when supported dependency sources are present; an online scan
never downloads or depends on an ecosystem-wide offline advisory database. GitHub repositories and
browser extensions without a supported package source bypass OSV-Scanner and report inventory and
hosted advisory checks as not applicable. This does not prevent their applicable static checks or
the overall package-security section from completing. Generic uploads follow the same rule. A
verified direct npm package still uses its registry-verified name and version for hosted matching,
but remains partial when a dependency inventory source is absent because only its top-level
coordinate is known. Hosted OSV refreshes use bounded retries, jitter, pagination, and one total
request budget shared by batch queries and advisory hydration. A stale cached result is used only
when the live refresh fails, and any live findings collected before that failure remain in the
partial result.
GitHub Advisory Database queries request the maximum supported page size and follow only trusted
cursor links on the fixed GitHub API host. Primary and secondary rate limits, pagination limits, and
provider deadlines are reported as partial coverage when earlier coordinates or findings were
already collected; they are never treated as a clean no-match.
The offline mode makes no provider API calls and does not download databases during a scan. The
worker must be deployed with a pre-seeded OSV-Scanner database at the configured local database
cache path. Matches returned by that local snapshot are included in the report rather than being
discarded.
Package intelligence is refreshed on a rescan even when the repository commit or file hashes have not changed. An authorized user can also request a package-intelligence refresh without a full rescan. Refreshes reuse the persisted component inventory and static evidence, re-query provider facts, and reproject the report from its preserved code-analysis snapshot; they do not submit repository files for duplicate evaluative AI analysis. A successful known-malware narrative is preserved only when its verdict, inventory, issues, and static findings are unchanged. Changed evidence uses deterministic fallback guidance until a later full report can generate a new bounded narrative. Repeated refresh requests are coalesced while Redis is available, with best-effort Celery inspection used during a Redis outage. Refresh provider work can overlap report generation, but repository row locking serializes their final report updates. A temporary queue outage returns a retryable service-unavailable response instead of claiming that work was queued.
Bounded package-intelligence backfill
Operators can opt in to a no-AI background backfill with
PACKAGE_SECURITY_BACKFILL_ENABLED=true. Each invocation selects only repositories whose latest
package-intelligence result is more than six hours old and is capped at 100 repositories. The
default cap is controlled by PACKAGE_SECURITY_BACKFILL_BATCH_SIZE, which accepts values from 1
through 100. A manual task invocation may request a smaller cap, but an invalid zero or negative
limit queues nothing.
The task records the backfill_no_ai trigger, requested batch limit, and queued count in structured
logs, while each resulting refresh remains an auditable persisted package-security run. Turn the
feature flag off to pause future batches; it does not alter prior reports or cancel already queued
refreshes. Backfill uses the existing privacy mode and provider rules, so offline mode queues no
network refreshes.
Artifact comparison and provenance
Each package scan builds a bounded local manifest of paths, sizes, and hashes. A later scan can compare that manifest with the preceding persisted package scan and report added, removed, or changed paths. The first scan has no comparison baseline, so diff coverage is not applicable rather than unavailable. When the optional public npm predecessor comparison is enabled, RepoRisk can retrieve the immediately preceding stable public release from the allowlisted npm registry, verify its published integrity digest, and retain only a bounded path-change summary. The downloaded predecessor is deleted when that comparison completes. Registry responses are streamed into a bounded buffer so an oversized response is rejected before the complete body is retained. Private packages, prereleases, missing registry digests, network failures, and limit hits are displayed as unavailable or partial rather than clean results. The same comparison summarizes entry paths, lifecycle-hook names, dependency additions/removals or changes, native-artifact paths, and the categories found by the existing static, graph, and bounded literal-decoding checks. It does not retain raw lifecycle commands or dependency specifiers in this comparison evidence.
For direct npm packages, a package.json-declared GitHub, GitLab, or Bitbucket repository URL and
gitHead are retained as package provenance claims. When the optional remote-source check is
enabled, an immutable GitHub gitHead claim can be verified through GitHub's fixed public API
endpoint without cloning, building, or executing source code. With its separate
source-correspondence option enabled, RepoRisk compares the submitted package's local Git blob
identifiers with the immutable GitHub tree metadata at that verified commit. It does not clone the
repository or download source files; limited or truncated comparisons remain partial, and a
matching overlap never proves that an artifact is safe or reproducibly built. Remote tree paths are
constrained to the submitted artifact root, remote entries and local reads are bounded, and
oversized local files are reported as incomplete context rather than read without limit. GitLab and
Bitbucket claims remain partial until their equivalent reviewed integrations are enabled. NPM
package-declared provenance and source correspondence are not applicable to GitHub repository,
browser-extension, or generic-upload submissions. Unavailable information is neutral and never
treated as proof of safety.
For Git repository rescans, the worker records lineage only when the scan history contains both the prior and current immutable commit IDs. A first Git scan has no lineage baseline, so lineage is not applicable. Lineage is also not applicable to uploads and browser extensions; RepoRisk never guesses that two artifacts are related. A missing current Git commit remains a partial coverage gap.
When optional npm reputation context is enabled, the worker derives only neutral release signals from the allowlisted public npm registry: release age, recent publish burst, and whether maintainer, publisher, or repository claims changed from the selected predecessor. It does not retain publisher names or email addresses, calculate a reputation score, or allow these signals to declare malware, change a grade, or prove a package safe.
When a public predecessor comparison is available, RepoRisk also records the bounded count and ratio of added, removed, and changed files as patch-size context only. Typosquat context remains unavailable until a reviewed cross-ecosystem candidate source is configured; it is never guessed from a package name alone.
For npm packages, the worker re-reads the exact release metadata from the allowlisted npm registry
before extraction. Extraction requires a supported SRI integrity value (sha256, sha384, or
sha512) and the downloaded tarball must match it. A legacy SHA-1 checksum is retained only as
compatibility evidence and cannot authorize extraction. The report distinguishes verified SRI
integrity from compatibility-only or unavailable registry evidence. Registry metadata is bounded
before parsing, malformed release timestamps fail closed, and partial extraction output is removed
when archive limits or safety checks reject a package.
Reading availability status
Reports keep execution status internally for audit and troubleshooting, but do not show a routine aggregate Status field beside the security verdict. When applicable checks cannot all finish, the section instead shows Coverage: Partial and identifies the affected coverage areas. If applicable static analysis is incomplete and no static analyzer completed meaningful work, it shows Static analysis: Inconclusive. These notices never replace or weaken deterministic findings: a known-malware verdict remains malicious even when other analysis is incomplete.
not_scanned means the report predates package analysis or the feature was not enabled for that
scan. complete means all configured checks applicable to the submitted source finished.
not_applicable means a check does not apply to that source or has no required first-run baseline;
it does not reduce completion. unavailable means an applicable data source could not be used.
partial means an applicable check produced some evidence but hit a documented failure, unreadable
input, truncation, or configured bound. Complete coverage is scoped to RepoRisk's supported
non-executing checks and is not a guarantee that the artifact is safe.
no_package_sources explains why RepoRisk skipped OSV-Scanner when no supported lockfile or
package/version source was present. For GitHub repositories, browser extensions, and generic
uploads, inventory and hosted advisory matching are not applicable in this case and do not prevent
the remaining applicable checks from completing. A registry-verified direct npm package still
queries hosted intelligence with its trusted top-level coordinate, but its inventory remains partial
because transitive dependencies could not be enumerated.
osv_scanner_unavailable is different: it means the worker could not successfully use OSV-Scanner
when a supported package source required inventory extraction, such as when its binary,
configuration, output, timeout, or filesystem boundary failed. It should be investigated in worker
logs under the package_security_inventory_unavailable event; it is not an empty-inventory result.
That event includes a bounded error_code, while raw scanner output, commands, paths, and exception
messages remain excluded from logs.
Online scans do not expose local_snapshot_unavailable. If OSV-Scanner emits valid package inventory
while reporting that its optional local advisory snapshot is absent, RepoRisk keeps the inventory and
uses hosted OSV intelligence without reporting a local-database coverage failure.
offline_database_unavailable remains specific to configured offline mode. Unrelated OSV-Scanner
failures remain unavailable and are not reclassified as successful inventory, even if the failed
process emitted parseable package JSON.
For a package submitted directly from npm, RepoRisk also records the exact top-level package name and version from the registry metadata that authorized the downloaded tarball. This allows OSV and GitHub malware checks to cover dependency-free releases even when OSV-Scanner finds no lockfile or other dependency source. A missing dependency source is reported as partial inventory rather than aborting static analysis or exact-package intelligence. Other uploads do not receive a public identity from an untrusted manifest alone. When a curated active-malware record produces an enforced verdict, RepoRisk marks the remaining per-file AI work as intentionally skipped and completes repository progress after the deterministic report and optional single narrative summary are generated.
When the optional Python packaging check is enabled, it inspects pyproject.toml build-backend
declarations, setup.py calls, and executable .pth imports as text only. It never imports a
module, runs a build backend, installs dependencies, or executes package code.
When the optional Ruby packaging check is enabled, it inspects gemspec native-extension declarations
and extconf.rb makefile entry points as text only. It never invokes Ruby, Bundler, extconf.rb,
or make.
When the optional Rust packaging check is enabled, it detects build.rs and procedural-macro
declarations as build-time entry points without invoking Cargo or compiling package code.
When optional Composer/PHP checking is enabled, it inspects composer.json scripts and plugin-related
metadata as text only. It never invokes Composer, PHP, or declared package scripts.
When optional .NET/MSBuild checking is enabled, it inspects project imports and build-target hooks as
text only. It never invokes dotnet, MSBuild, or project targets.
When optional Maven/Gradle checking is enabled, it identifies Maven plugin declarations and Gradle task/plugin blocks as build-time entry points without invoking Java, Maven, or Gradle.
When optional GitHub Actions checking is enabled, it identifies workflow run steps and reusable
workflow calls as execution entry points without running actions, shells, or referenced code.
For each optional ecosystem scanner, no matching entry-point files means that scanner is not applicable. If matching files exist and every supported file is examined, its coverage is complete. Unreadable or oversized relevant files, symlinks, finding or file limits, and malformed applicable metadata remain partial. Complete means the supported non-executing checks finished; RepoRisk does not claim that dynamic build or runtime behavior was executed or fully understood.
OSV and GitHub provider checks are recorded separately. A provider outage is shown as unavailable or partial; it is never represented as a clean no-match. Each provider record includes its own request, queried-coordinate, match, and error details.
See Risk Scoring for the existing code-analysis score and grade.