Skip to content

refactor(cli): adapters, util and toolchain on traust-core's tool doors (XWING-2440) - #286

Merged
smith-xyz merged 2 commits into
traust-security:developmentfrom
smith-xyz:refactor/traust-tools-on-core
Oct 27, 2026
Merged

smith-xyz merged 2 commits into
traust-security:developmentfrom
smith-xyz:refactor/traust-tools-on-core

Conversation

@smith-xyz

@smith-xyz smith-xyz commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #279 (lane a, refactor/traust-core-loader @ 9189e08). Until #279 merges, this diff includes it; this lane's commit is the last one (4f13b3a).

Plan: plans/traust-standardization/0-continuous-ops/17-shaun-traust-core-only.md (part)

Chain: traust-core#225 (feat/context-tool-doors @ e8f4b61, the tool report doors; traust pins core to it) ← #279 (lane a) ← this PR (lane b). Lanes c–e stack on #279 as well, and lane f closes the plan.

What

Wave 2 lane (b): traust adapters, traust util elf|safe-exec, traust admin toolchain and traust.lib no longer import traust-engine. Each op calls one traust-core door from traust-core#225 (traust_core.v1.context.tool_reports). The door returns the same document the engine path wrote, and the CLI keeps its flags, messages, files and exit codes. The doors are interim: the Tool execution API follow-up and plan 19 (preflight) reshape them later.

traust op Was (engine) Now (core)
adapters opengrep _execute_opengrep_scan + HarnessEngine allowlist/locations opengrep_report on the pack load() registers (or --rule-pack)
adapters govulncheck _run_govulncheck + engine profile map govulncheck_report on ctx.safe_exec
adapters checkov (KHS) adapters.checkov.Scanner k8s_hardening_report
adapters gitleaks adapters.gitleaks internals + engine locations gitleaks_report (--config, else locations.gitleaks_config)
adapters osv (repo, --sbom, --grype) adapters.osv / osv_sbom, OsvClient, DepsDevClient osv_report / osv_sbom_report; the API path registers the feeds registry (register_feeds) and uses ctx.advisories / ctx.deps_dev
adapters yara _execute_yara_scan yara_report
adapters joern _run_joern_scan + emit joern_report; the CLI writes the file as emit did
adapters crypto-probe, crypto-audit adapters.crypto_probe, crypto_audit retired: duplicates of adapters crypto-source / crypto-image / crypto-cluster (core's CodeFactsService.crypto*); skill/doc lines repointed
util elf _util.elf.ElfAnalyzer elf_report
util safe-exec _util.safe_exec + engine profile map, run(honor_bypass=True) ctx.safe_exec (profile, validate_argv, vet_command_string, run_segments); the SAFE_EXEC_DISABLED bypass moved here: stderr shout + per-user 0700 log, same line and path as before
`admin toolchain setup doctor fetch-dbs`
traust.lib re-exported ElfAnalyzer & co. from the engine re-exports dropped: nothing imported them (only importing traust.lib.* ran the engine import)

Exit codes are kept: core ValidationError / IntegrityError (refused rules sources, --rule-allow) → 2, as the engine's sys.exit(2) was. A rule pack that isn't a directory (ConfigError) → 1, as the engine's ValueError was. A missing safe-exec/toolchain config file → 2 (config not resolvable: …).

src/traust/_tools.py stays: context.py's two PENDING(pack: …) branches still need it, and adapters govulncheck|osv take harness_version (the engine package version, unchanged) from it. Lane (f) deletes it.

Ratchet: .ratchet.json only shrank: engine-internal dropped from adapters.py (3), util.py (2), toolchain.py (1) and lib/__init__.py (1); all four entries are gone. tests/test_engine_imports.py's baseline shrank to match (adapters.py 15, util.py 2, toolchain.py 1, lib/__init__.py 1 removed). Nothing was added.

Behaviour changes

From core's tools (traust-core #205/#211/#212/#225):

Change Who is affected
Scrubbed env. Version/database probes run with PATH HOME LANG LC_ALL TMPDIR GRYPE_DB_CACHE_DIR, scanners with config.process.keep_env (default PATH HOME LANG LC_ALL TMPDIR), never the whole parent env. A proxy or token a tool needs must be named in process.keep_env every adapters op, admin toolchain
No env grant; no shell or env segment even in warn mode. util safe-exec run in warn mode refuses such a segment with rc 126 [safe_exec blocked: …]; the engine ran anything in warn mode util safe-exec run with SAFE_EXEC_MODE=warn
Profiles required. No built-in fallback profiles: a missing or invalid safe-exec-profiles.yaml is exit 2 naming the file, and an unknown profile is exit 2 (unknown safe_exec profile …); the engine fell back to built-ins / raised KeyError util safe-exec, adapters govulncheck
A missing binary / timeout reads "<tool> not found on PATH (<bin>)" / "<tool> timed out after Ns" (exit 1 as before) `adapters gitleaks
opengrep: an insecure/movable git ref, a missing or empty --rule-allow and an unverifiable rules cache now exit 2 with ERROR: … (the engine's SystemExit(msg) exited 1); --rules auto and a missing local source stay 2 adapters opengrep
yara: every rules-source refusal is exit 2 (some were SystemExit(msg) = 1) adapters yara
opengrep's coverage block counts the pack the scan used; with --rule-pack the engine counted the locations/bundled pack adapters opengrep --rule-pack
The opengrep and yara reports' version is traust-core's report readers
osv (API): --timeout no longer sets the per-request timeout (core's HTTP policy does); deps.dev answers are cached in the object store instead of <feeds cache>/deps-dev. With both --sbom and --grype unreadable, the --grype error is reported first adapters osv
gitleaks with no usable rules (--config not a file, remote locations.gitleaks_config) is ERROR: … exit 1 instead of a traceback (also exit 1) adapters gitleaks
Grouped ops that loaded nothing now build core's Context (joern, yara), so an invalid core.yaml exits 2 there `adapters joern
admin toolchain doctor's OK stamp is ToolStamp.render (build date only) doctor output

From retiring adapters crypto-probe / crypto-audit (gap table: evidence/17-crypto-gap-table.md; gaps listed, not ported):

Gap (row) Effect
4: GO_FIPS_BACKEND extra.crypto_backend_swap / note not in the core census (read only by a traust fixture)
20, 24: probe_sbom on any CycloneDX file, file="sbom:<purl>" crypto packages come from the recorded SBOM rows (crypto-source --sbom), by package name, no purl
26: audit envelope tier, target, timestamp, summary not in the census document
27: --component (governance-chain keying) dropped; NEED(contracts: governance-chain artifact schema) already in core
28: repo_dir, sbom, container_id, labels map, mode metadata partial: labels are IMG_LABEL facts
30, 31: image --runtime, --platform not offered by crypto-image
35: every image package via rpm/dpkg/apk inside the image by design: core never runs the image; crypto packages come from the image SBOM
42: crypto-audit-scope.jsonl append logged plus ClusterCensus.scope; no traust reader
11, 14 (unverified) Cargo without a lock / .NET projects depend on what syft catalogues

pqc_facts.py still imports the engine's crypto_probe. That belongs to lane (e).

Changed tests

Test Old → new Why
tests/test_adapters_osv_cli.py (whole file) _args gains config_home (None by default); traust_engine.adapters.osv/osv_sbom imports → traust_core.v1.providers.tools.osv/osv_sbom the CLI runs on core's doors, so the stubs move to the functions the doors call
…::test_complete_scan_writes_candidates, test_degraded_scan_exits_3_and_warns, test_partial_transitive_coverage_is_noted, test_an_unusable_api_is_an_error_exit_2, test_the_api_is_the_default_access stub scan_api(repo, client, resolver=None) → scan_api(repo, advisories, clock, resolver=None); assertions unchanged core's signature
…::test_air_gapped_access_runs_osv_scanner_offline stub run_scanner(binary, repo, timeout, *, freeze) returning text → run_scanner(process, binary, repo, timeout, *, freeze) returning bytes; an osv-scanner stub on PATH; assertions unchanged core's signature; the door checks the binary is on PATH before running it
…::test_air_gapped_without_osv_scanner_is_an_error_exit_2 a run_scanner stub raising FileNotFoundError → an empty PATH; assertions unchanged the door's presence check now produces the "not found" failed body
…::test_air_gapped_with_no_offline_database_is_an_error_not_clean same stub change (bytes) + osv-scanner on PATH; assertions unchanged core's signature
…::test_sbom_scan_marks_grype_duplicates stub scan_sbom(doc, client) → scan_sbom(doc, advisories, clock); _with_stats → with_stats; assertions unchanged core's names
…::test_air_gapped_sbom_scan_runs_osv_scanner_on_the_filtered_packages stub run_offline(binary, cdx, timeout) reading the CycloneDX file → run_offline(process, binary, packages, timeout) building it with osv_sbom.cyclonedx(packages); same purl assertion core writes the CycloneDX file itself
…::test_air_gapped_sbom_scan_without_a_database_fails same stub change (bytes) + osv-scanner on PATH; assertions unchanged core's signature
…::test_deps_dev_is_on_by_default isinstance(_deps_dev_resolver(...), DepsDevClient) and its base_url → a home whose feeds.yaml has the shipped deps_dev source gives a Context with deps_dev set deps.dev is core's ctx.deps_dev (from the feeds registry); traust's resolver is gone
…::test_deps_dev_opt_out _deps_dev_resolver(...) is None for access: disabled → ctx.deps_dev is None for access: disabled same
…::test_a_config_without_deps_dev_says_so _deps_dev_resolver prints the NOTE → _note_unresolved_manifests prints the NOTE (same text) the NOTE stays in traust; the resolver moved
…::test_resolution_is_reported traust deps_dev_settings stub → a config home with deps_dev enabled; assertions unchanged the resolver now comes from the feeds registry
tests/test_engine_imports.py baseline adapters.py 15, util.py 2, toolchain.py 1, lib/__init__.py 1 → removed the imports are gone (baseline only shrinks)
  • A person reviewed each changed test and the behaviour change is intended

New tests: tests/test_adapters_core_parity.py (27 cases: each op runs over the same fixtures and fake binaries through the engine path and the core door, outputs equal; marked wave 2: removed by lane (f) because it imports the engine), and tests/test_tools_on_core_cli.py (15 cases: exit-code mapping for opengrep/yara/gitleaks, joern file and soundness, KHS summary, util elf, util safe-exec check/run/warn/bypass shout and 0700 log/list/unknown/missing profiles, the doctor on the core toolchain).

Prose changes

File Hunk Kind
crypto-analysis/SKILL.md § Integration / From pqc-readiness "probes with adapters crypto-probe until its port; this skill with adapters crypto-source" → "pqc-readiness and this skill probe with adapters crypto-source" 1 (command)
pqc-readiness/SKILL.md § Two-layer probe model adapters crypto-probe → adapters crypto-source; table rows crypto_probe.py / crypto_audit.py → crypto-source / crypto-cluster with their commands 1 (command)
validate-findings/SKILL.md crypto posture probes adapters crypto-audit cluster → adapters crypto-cluster; "emits crypto-audit/v1 (tier: runtime)" → "emits the runtime crypto census" 1 (command, output)
docs/cli-reference.md, docs/tooling-and-structure-files.md the two retired ops removed / folded into the crypto-source / -image / -cluster row 1

docs/skills.md regenerated unchanged (committed with SKILLS_DOC_WAIVER). The *-crypto-audit.json file names and the crypto-audit deterministic_steps key are output names, not the op, so they're unchanged.

Legacy files

File / symbol Verdict Where / why
cli/groups/adapters.py engine calls Reuse core doors (table above)
adapters.CRYPTO_PROBE, CRYPTO_AUDIT Retire duplicates of crypto-source/-image/-cluster (gap table)
adapters._deps_dev_resolver, _osv_binary_doc, _sbom_body, _failed_binary_doc, AIR_GAPPED_HELP, OSV_SCANNER_OK_EXITS moved into osv_report / osv_sbom_report in core (behaviour identical)
cli/groups/util.py engine safe_exec / elf Reuse ctx.safe_exec, elf_report; bypass ported here from engine safe_exec.run(honor_bypass=True) / _log_bypass
cli/toolchain.py engine toolchain Reuse ctx.toolchain + doors
lib/__init__.py ELF re-exports Retire no importer

Markers

Removed: six # wave 2 lane (b) markers (adapters ×3, util ×2, toolchain ×1). Added: wave 2: removed by lane (f) once traust-engine is no longer installed. (parity test docstring). PENDING/NEED: none added or removed.

Checks (local; CI not watched)

  • make lint-fix && make test: 2737 passed, 2 skipped (Postgres on traust_test_w2_b)
  • uv run traust check docs-consistency: consistent
  • consumer ratchet as CI runs it (RATCHET from ci.yml, reference upstream/development:.ratchet.json): clean, baseline only shrank
  • pre-commit (skill alignment, security posture) and pre-push hooks: passed
  • Real-binary spot check: opengrep 1.25 and gitleaks 8.30 through the engine path and the core doors: equal

Left for later lanes

  • Lane (e): pqc_facts.py (crypto_probe), validate-findings' dynamic safe_exec
  • Lane (f): delete _tools.py (after the PENDING(pack: …) branches; also replace harness_version in adapters govulncheck|osv), _registry.load_engine, tests/test_adapters_core_parity.py; gate texts (S10 names traust_engine._util.safe_exec)

Reviewer checks

  • Each moved piece lands on the core owner the api-spec names; no engine code copied into traust
  • Security content (rule packs, allowlists, scanner configs) stays pack config passed by path

Quality checklist

  • Comments only for the non-trivial
  • Docstrings don't bloat
  • Don't reinvent
  • Shared logic is a helper, in one place
  • Tests cover the edges
  • Integration tests (suite on Postgres)
  • Minimal, clean code
  • Queries (n/a)
  • Tests change only on purpose, and a person said so (see Changed tests)

@smith-xyz
smith-xyz requested a review from a team as a code owner October 27, 2026 16:50
@openshift-ci

openshift-ci Bot commented Oct 27, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: smith-xyz

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added approved Indicates a PR has been approved by an approver from all required OWNERS files. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Oct 27, 2026
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 27, 2026
@smith-xyz
smith-xyz merged commit c155a74 into traust-security:development Oct 27, 2026
4 of 5 checks passed
smith-xyz added a commit to smith-xyz/traust that referenced this pull request Oct 27, 2026
smith-xyz added a commit to smith-xyz/traust that referenced this pull request Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant