Built-in adapter profiles¶
RigorFoundry supplies three immutable repository-audit profiles. They remove adopter-specific command reconstruction while retaining explicit consent, tracked-input identity, tool identity, and fail-closed interpretation.
| Profile | Fixed analyser | Claimed domain | Accepted complete result |
|---|---|---|---|
semgrep-local-json-v1 |
local Semgrep rules | application-security |
strict JSON with at least one scanned path and no analyser errors |
trivy-repository-json-v1 |
offline misconfig,secret filesystem scan |
application-security |
Trivy schema 2 JSON with at least one result target |
osv-lockfile-offline-json-v1 |
offline lockfile scan against a verified local OSV snapshot | supply-chain |
strict OSV JSON with at least one scanned lockfile and no scan errors |
The Trivy profile does not load or update a vulnerability database and does not claim dependency-vulnerability, CVE, SBOM, licence, or supply-chain coverage. Its fixed scanners are repository misconfiguration and secret detection only. Use a separately specified and evidence-bound adapter when those other domains are required.
Policy declaration¶
Repository policy schema 1.3 accepts this strict profile form in
native_audits:
{
"name": "semgrep-local",
"profile": "semgrep-local-json-v1",
"configuration_path": "config/semgrep.yml",
"target_paths": ["src", "tests"],
"timeout_seconds": 120,
"scope": "full",
"working_directory": ".",
"required": true
}
{
"name": "trivy-repository",
"profile": "trivy-repository-json-v1",
"configuration_path": "config/trivy.yml",
"target_paths": ["."],
"timeout_seconds": 120,
"scope": "full",
"working_directory": ".",
"required": true
}
{
"name": "osv-lockfiles",
"profile": "osv-lockfile-offline-json-v1",
"configuration_path": "config/osv-database.json",
"target_paths": ["requirements.txt", "package-lock.json"],
"timeout_seconds": 120,
"scope": "full",
"working_directory": ".",
"required": true
}
Profile declarations cannot override the executable, argv, parser, or domains.
The working directory is always .. Trivy accepts exactly one target; Semgrep
and OSV accept one or more. For OSV, configuration_path names a tracked strict
JSON database manifest, not an OSV TOML configuration. The manifest uses schema
1.0 and contains a sorted, unique list of ecosystem, all.zip byte length, and
SHA-256 records plus a canonical aggregate database_digest. Configuration and
targets must be canonical
repository-relative paths. The profile declaration, immutable profile, and
derived command are independently digest-bound. Run construction and durable
store/load require the ordered evidence name, required flag, specification
digest, and profile identity to match every policy adapter selected for a full
run. The context-free AuditRunAttestation.from_dict() parser validates record
integrity only; callers must use the campaign store boundary to validate an
imported attestation against its report policy and campaign contract. Campaign
comparison repeats the adapter-policy relation check for every supplied run;
context-invalid evidence is retained as unresolved divergence and can never
produce a promotion-eligible comparison record.
Exact input projection¶
Execution requires a clean tracked worktree. RigorFoundry builds a temporary read-only snapshot from the Git inventory and includes only the declared configuration and target trees. Every selected entry must be a present, single-link regular tracked file whose observed byte count, SHA-256, and Git blob identity match the frozen inventory. Symlinks, gitlinks, ignored files, untracked files, replacement races, empty target sets, and files larger than 32 MiB fail closed. The aggregate selected input is limited to 256 MiB.
The input digest binds HEAD, tree, Git object format, complete tracked-content digest, configuration path and digest, ordered targets, and every copied file record. Temporary host paths are excluded from durable identity and removed after execution.
The OSV database bytes are deliberately outside the repository snapshot. The
operator must set OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY to an absolute host
directory containing the official cache layout
osv-scanner/<ecosystem>/all.zip. RigorFoundry opens every directory without
following symlinks, requires single-link regular archives, verifies exact size
and SHA-256 against the tracked manifest, rejects unsafe or unbounded ZIP
members, and copies only verified archives into the private workspace. The
tracked manifest digest binds the expected database to the profile
configuration; the sandbox repository identity additionally binds its canonical
database_digest. Missing or changed database bytes produce unavailable
evidence and are never fetched automatically.
Sandbox and executable identity¶
The selected executable is resolved from fixed runtime roots, opened through a
retained descriptor, checked for trusted ownership and unsafe write bits, and
mounted read-only at /run/rigor-adapter-tool. The tracked snapshot is mounted
read-only at /workspace. Bubblewrap unshares the network and all namespaces,
uses UID/GID 65534, clears the environment, provides a private tmpfs, disables
telemetry and Semgrep metrics/version checks, and binds the system CA bundle's
content digest. Version output and the executable bytes are recorded and the
retained descriptor is revalidated after the scan.
Semgrep runs local configuration only:
semgrep scan --config CONFIG --json --error --metrics off
--disable-version-check --no-rewrite-rule-ids TARGET...
Trivy runs without update or network-backed database acquisition:
trivy filesystem --config CONFIG --format json --exit-code 1
--skip-db-update --skip-java-db-update --skip-check-update --offline-scan
--scanners misconfig,secret --include-non-failures TARGET
OSV-Scanner runs only against the verified local snapshot:
osv-scanner scan source --offline --format=json --verbosity=error
--no-resolve --all-packages --lockfile TARGET...
These wrapped lines are descriptive; the production registry owns one exact argv tuple and its profile digest.
Evidence and enforcement¶
Adapter result schema 2.0 embeds profile-evidence schema 1.0. The nested record binds the profile and profile digest, tool version and version-output digest, configuration/input/output digests, finding count, scanned-target count, and a finite status/reason pair:
| Status | Complete | Pass | Meaning |
|---|---|---|---|
clean |
yes | yes | Valid output, scanned targets, zero findings |
findings |
yes | no | Valid output with one or more findings |
partial |
no | no | Timeout, truncation, malformed output, analyser errors, invalid return code, or no scanned target |
unavailable |
no | no | Executable, trustworthy version evidence, or the exact OSV database snapshot was unavailable or invalid |
Imported records must reproduce the outer execution output digest and exact return-code/timeout/truncation relation. Unavailable evidence has return code 126, zero output bytes, zero result counts, no tool-version claim, and neither timeout nor truncation. These relations are validated after digest recomputation; a self-consistent but cross-wired nested record is rejected. Invalid-output, timeout, and truncation reasons also require zero result counts; no-scanned-target evidence requires a zero scanned count; invalid-return-code evidence requires scanned targets and an outer return code that contradicts the finding-derived expected code. Scan-error evidence is valid only for Semgrep and OSV because the Trivy parser has no corresponding production outcome.
Only complete evidence contributes declared domain coverage. A required
findings, partial, or unavailable result blocks enforcement; findings
remain complete evidence and are not relabelled as execution failure. Campaign
schema 1.9 retains the exact nested evidence in each run attestation.
Enforcement schema 1.4 names the profile status and reason in its blocker.
Digest-dependency schema 1.6 binds inventory to profile evidence and binds that
evidence into the run attestation; policy remains bound through the separate
campaign contract that the attestation also embeds.
Installation and CI¶
Semgrep is supplied by the hash-locked Python environment. CI installs Trivy
0.72.0 and OSV-Scanner 2.4.0 for Linux x86-64 with:
python -m tools.install_trivy
python -m tools.install_osv_scanner
The installer retrieves only the immutable GitHub release assets, bounds both
downloads, verifies the pinned checksum-list SHA-256, verifies the pinned
archive SHA-256, requires that the checksum list names that exact archive, and
extracts only the sole regular trivy member. It never calls extractall.
The OSV installer applies the same bounded-host, pinned checksum-document,
exact-asset SHA-256, and atomic mode-0700 policy to the immutable Linux
binary. The resulting binaries are atomically installed. These downloaders are
source-checkout/CI tools, not part of the local-only runtime wheel. PyPI adopters
must provision the exact analyser through an independently verified deployment
step. Other platforms need an independently pinned installer before this
profile can be claimed available.
The fixed contracts follow the upstream Semgrep local/CLI scan guidance, Trivy filesystem target, and Trivy reporting, OSV offline mode, and OSV output interfaces. Installer pins refer to the immutable Trivy 0.72.0 release, and OSV-Scanner 2.4.0 release, not to moving latest-release URLs.
Measured performance¶
benchmarks/adapter_profiles.py measures all three real tools through the same
descriptor-bound Bubblewrap execution path against a committed seven-file
fixture with Dockerfile, Python, requirements, and Rust targets. It emits
machine-readable JSON including
host, Python and tool versions plus minimum, median, and maximum wall time.
.venv/bin/python benchmarks/adapter_profiles.py --iterations 3
On 2026-07-19, one bounded integration run per profile on a non-isolated Linux 6.17 x86-64 workstation (12 logical CPUs, CPython 3.12.3) measured:
| Profile/tool | Minimum | Median | Maximum |
|---|---|---|---|
| OSV-Scanner 2.4.0 | 2315.169 ms | 2315.169 ms | 2315.169 ms |
| Semgrep 1.170.0 | 8804.784 ms | 8804.784 ms | 8804.784 ms |
| Trivy 0.72.0 | 3270.411 ms | 3270.411 ms | 3270.411 ms |
Results are non-isolated workstation observations, not throughput guarantees. External analyser startup dominates this orchestration boundary. There is no native Rust implementation or polyglot counterpart of the Python control-plane runtime to compare; the fixture includes multiple languages only to verify that input projection is language-neutral. Adding a native runtime is a separate architecture decision and must establish evidence-schema parity before making performance claims.