Skip to main content

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_applicable verdict 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.