Skip to content

[self-audit] supply-chain department findings against this repository #35

Description

@2233admin

Self-audit of this repository by its own supply-chain department, run under the T1–T4 audit kernel. Score 7.5/10, coverage high. Every finding below is contract-validated: code-intel audit --operation validate grounded all cited paths and line ranges against the live tree.

Open

supply-chain-001 — medium / high confidence / confirmed

Unpinned runtime pip installs in the CI job that holds ANTHROPIC_API_KEY

Evidence: .github/workflows/skill-check.yml:44-45, .github/workflows/skill-check.yml:47-49, .github/workflows/skill-check.yml:158

The score-skills job installs the newest available anthropic and pyyaml from PyPI at run time with no version pin or hash verification, then runs a step with ANTHROPIC_API_KEY in its environment that imports yaml. The anthropic package is installed but never imported by the inline heuristic script (the comment at line 93 says LLM scoring is future work), so it is pure attack surface today. Whatever PyPI serves on the day of the run is what executes next to the secret.

Failure scenario: A malicious release of pyyaml (or of the unused-but-installed anthropic package) is published to PyPI after a maintainer-account compromise. The next PR touching skills/** triggers skill-check; pip resolves the new version; the scoring step runs import yaml with ANTHROPIC_API_KEY exported, and module import-time code reads the variable and exfiltrates it over HTTPS. The key is then consumed for paid API usage until someone notices billing anomalies and rotates it.

Minimal fix (minutes): Pin exact versions with hashes (a small requirements-ci.txt consumed via pip install --require-hashes -r), and delete the anthropic install until the LLM-based scoring path actually exists.

supply-chain-002 — medium / high confidence / confirmed

Unpinned Chocolatey package executes install scripts on the release runner before packaging

Evidence: .github/workflows/release.yml:56-58, .github/workflows/release.yml:8-9, .github/workflows/release.yml:121-136, .github/workflows/ci.yml:39-41

The release job installs ripgrep from the Chocolatey community repository with no version pin. Chocolatey packages execute an embedded chocolateyInstall.ps1 with runner privileges at install time, and the ripgrep package is community-maintained rather than published by the upstream author. In release.yml this execution happens in the same contents:write job, before the step that packages target/release/code-intel.exe and computes the sha256 sidecar, so a malicious package can influence the exact bytes that ship.

Failure scenario: The Chocolatey ripgrep package maintainer account is compromised and a trojaned package version is pushed. The next v* tag triggers release.yml; choco install ripgrep runs the malicious install script, which overwrites target\release\code-intel.exe (built at line 74, consumed at line 132) with a backdoored binary. The Package step then hashes the tampered zip, the manifest and .sha256 sidecar are internally consistent, gh uploads everything, and bootstrap.py on every consumer machine verifies the (matching) digest and installs the backdoor.

Minimal fix (minutes): Pin the package version (choco install ripgrep --version <exact> -y --no-progress), or fetch the upstream ripgrep GitHub release asset directly and verify a hardcoded sha256 before adding it to PATH.

supply-chain-003 — medium / high confidence / confirmed

Installer auto-installs and auto-upgrades repowise from PyPI unpinned, then string-patches the installed package

Evidence: install-code-intel-pipeline.ps1:256-259, install-code-intel-pipeline.ps1:852, install-code-intel-pipeline.ps1:842, install-code-intel-pipeline.ps1:565-602

Every installer run pulls whatever version of repowise PyPI currently serves, under --upgrade semantics, with no version pin or hash verification - a risk the install plan text itself already classifies as MEDIUM. The follow-on thinking-block patch then modifies the installed third-party package in place based on an exact upstream string (documented in overlays/repowise/README.md), so unpinned silent upgrades both widen the supply-chain window and can silently change pipeline docs-generation behavior when the patch no longer applies.

Failure scenario: The repowise PyPI project is compromised (maintainer account takeover or malicious point release). An operator reruns install-code-intel-pipeline.ps1 - a documented routine action, since uv tool upgrade repowise wipes the thinking patch and the installer restores it. pip installs the malicious version under the user account; its code executes the next time the pipeline invokes repowise for semantic indexing, with access to the operator's environment including any provider API keys, on every machine bootstrapped this way.

Minimal fix (minutes): Pin an exact version in Invoke-PipInstall's repowise call (repowise==X.Y.Z) and move the pin deliberately; drop --upgrade from the default path.

supply-chain-004 — low / high confidence / confirmed

GitHub Actions referenced by mutable tags instead of commit SHAs

Evidence: .github/workflows/ci.yml:19, .github/workflows/release.yml:17, .github/workflows/skill-check.yml:19

Every third-party action reference uses a mutable major-version tag (@v4/@v5) rather than a full commit SHA. Only first-party actions/* are used, which materially lowers the risk, but a moved tag still substitutes workflow code silently - including in the release job that holds a contents:write token and produces the published artifact.

Failure scenario: The actions org or one of these action repos suffers a compromise that repoints the v4 tag (the tj-actions/changed-files incident pattern). The next tag push runs attacker-controlled checkout code with contents:write in release.yml, letting it tamper with the tree before build and packaging, and the published zip plus its self-consistent sha256 carry the result to every bootstrap.py consumer.

Minimal fix (minutes): Pin each uses: to the full 40-hex commit SHA with the version as a trailing comment (e.g. actions/checkout@<sha> # v4.x.y).

supply-chain-005 — low / high confidence / confirmed

cargo build/test/check run without --locked in CI and release, so lockfile drift is silently re-resolved

Evidence: .github/workflows/ci.yml:47-61, .github/workflows/release.yml:64-74, tools/New-BetaPackage.ps1:82-84

Without --locked, cargo silently re-resolves and rewrites Cargo.lock whenever the manifest and lockfile disagree (manifest bumped without lock update, or a locked version yanked). The tested and released binary can then embed dependency versions that were never committed or reviewed. The only --locked usage in the pipeline (cargo metadata --locked in New-BetaPackage.ps1) runs post-build, when the lock on disk already reflects the re-resolution, and release.yml does not use New-BetaPackage at all. The dependency surface is tiny today (serde_json = "1" floating over 12 locked crates), which caps the blast radius but not the mechanism.

Failure scenario: A PR raises serde_json's requirement (or serde_json's locked 1.x is yanked) without regenerating Cargo.lock. CI re-resolves at build time to the newest compatible release and goes green; a tag is cut; release.yml builds and ships a binary whose dependency set matches no committed lockfile, while the release manifest records only the rustc version - the drift is invisible in review and unreproducible from the tree, defeating the exact goal the rust-toolchain.toml pin comment states.

Minimal fix (minutes): Add --locked to every cargo check/test/build invocation in ci.yml and release.yml.

supply-chain-006 — low / high confidence / confirmed

skill-check.yml is the only workflow without an explicit permissions block

Evidence: .github/workflows/skill-check.yml:1-13

The workflow token's scope falls back to the repository/organization default instead of being pinned to least privilege as the other two workflows do. Combined with finding supply-chain-001 (unpinned runtime pip installs in this same workflow), the effective blast radius of a compromised PyPI dependency depends on a repo setting that is invisible in the tree and can regress silently.

Failure scenario: The repository's default workflow permissions are (or are later flipped to) read-write - the pre-2023 GitHub default that still applies to older repos and some org policies. A compromised pyyaml release from finding supply-chain-001 now executes with a GITHUB_TOKEN capable of pushing commits or creating releases, escalating a key-theft incident into repository tampering.

Minimal fix (minutes): Add permissions:\n contents: read at the top of skill-check.yml.

supply-chain-007 — low / high confidence / confirmed

Release integrity is single-channel: assets are replaceable post-publication and carry no provenance attestation

Evidence: .github/workflows/release.yml:186-188, .github/workflows/release.yml:133-145, skills/code-intel-pipeline/scripts/bootstrap.py:149-157, rust-toolchain.toml:2-5

The zip, its .sha256 sidecar, the release manifest, and the API digest that bootstrap.py verifies all live in the same GitHub release and update together. Anyone with contents:write can replace all of them consistently - and the workflow itself ships the --clobber path that does exactly that for an existing tag. No build-provenance attestation or signature ties a published asset to the tagged source and the workflow run that built it, so consumers cannot distinguish the original release build from a later replacement.

Failure scenario: A repo PAT with contents:write leaks (or a collaborator account is phished). The attacker runs gh release upload v0.5.1 tampered.zip tampered.zip.sha256 tampered.release-manifest.json --clobber - no tag push, no workflow run, no CI gate. GitHub recomputes the asset digest; every subsequent bootstrap.py install verifies the now-matching digest and installs the tampered pipeline; nothing in the consumer-side chain flags the swap.

Minimal fix (hours): Add actions/attest-build-provenance for the release zip in release.yml and document gh attestation verify in the install docs; stop using --clobber for already-published stable tags (fail instead, forcing a new patch release).

supply-chain-008 — low / high confidence / confirmed

Four capability digest pins in integrations.json declare no evidence inputs, so they cannot be recomputed or drift-checked

Evidence: command: sha256 recomputation script over all 42 integrations: 14 declare implementation.toolchainDigests; 10 match the sha256 of their declared toolchainDigestEvidence.inputs (raw or LF-normalized); 4 declare digests with no toolchainDigestEvidence block at all; 0 mismatches, orchestration/integrations.json:1, crates/code-intel-cli/src/capability.rs:784-798, command: grep over crates/ for integration-iterating digest tests: per-capability recompute tests exist only for project_orientation.rs:383, native_code_evidence.rs:723, doctor_adapter.rs:576; no test iterates every digest-declaring integration

toolchainDigests are this pipeline's own supply-chain pin - they bind a capability implementation to the sha256 of its source inputs. Ten of fourteen declaring entries also declare the inputs they hash (toolchainDigestEvidence.inputs) and all ten recompute correctly against the live tree. Four entries declare digest values with no inputs, so neither a human, a test, nor this audit can verify what they pin; runtime validation checks only the shape of the digest strings. An unverifiable pin provides the appearance of integrity binding without the check.

Failure scenario: The doctor envelope implementation is refactored. Its digest in integrations.json is stale but nothing can detect it - shape validation passes, no per-capability test covers it, and there are no declared inputs to recompute from. A later compatibility-retirement decision or audit run cites the registry's digest binding as evidence that the implementation is unchanged, and the fail-closed evidence chain now contains a silently false attestation.

Minimal fix (hours): Add toolchainDigestEvidence.inputs for doctor.envelope.compat, repository.snapshot.compat, advisory.workflow-recommend.compat, and inventory.rg.compat, and correct their digests to the current source sha256.

supply-chain-009 — info / medium confidence / suspected

Sentrux Pro auto-activation shim's authorization is asserted in-tree but not substantiated by any upstream license text

Evidence: tools/sentrux-shim/sentrux-shim.ps1:107-119, docs/code-intel-architecture.md:56, install-code-intel-pipeline.ps1:846, command: grep for sentrux licensing/provenance statements across all .md files: 44 mentions of sentrux near pro/license/vendor terms, none containing upstream license text, a vendor authorization statement, or a link to sentrux's licensing terms; tools/sentrux-shim/ contains no README or license note

The shim fabricates a local pro-tier license file for the external sentrux tool by default on every bootstrapped machine and injects a 'pro' command into the real tool's help output. The tree consistently frames this as open-source local activation, but contains no upstream license text, vendor reference, or provenance note for sentrux itself that substantiates the claim that pro-tier features may be locally auto-activated. Whether this is legitimate depends entirely on sentrux's actual licensing terms, which are not evidenced in-tree, so this is recorded rather than judged.

Failure scenario: Sentrux's pro tier turns out to be (or later becomes) a commercial entitlement whose license does not permit local activation. Every machine bootstrapped by install-code-intel-pipeline.ps1 has silently auto-activated it, exposing operators to license violation, and the pipeline's pet-modality structural evidence depends on feature output (what-if, evolution details) that a vendor enforcement change could invalidate across the fleet at once.

Minimal fix (minutes): Add a provenance note in tools/sentrux-shim/ naming the upstream project, its license, and the specific clause or statement permitting local Pro activation - or flip auto-activation to explicit opt-in (SENTRUX_AUTO_PRO defaulting off).

Coverage

All three Cargo.toml manifests and both Cargo.lock files parsed (workspace, code-intel-cli, prototypes/session-observability); rust-toolchain.toml read; all three .github/workflows files read line-by-line (ci.yml 408 lines, release.yml 207, skill-check.yml 205); 144 .ps1/.py/.yml script files swept for download-and-execute, remote-fetch, and package-install patterns with targeted confirming reads of bootstrap.py, install-code-intel-pipeline.ps1, Invoke-ProviderRuntimeInventory.ps1 (data-only local-endpoint fetch, confirmed benign), Install-SentruxVlangOverlay.ps1 and Invoke-RepomixCodePack.ps1 (zero remote calls), tools/New-BetaPackage.ps1, and tools/sentrux-shim/*. All 14 integrations declaring toolchainDigests in orchestration/integrations.json were recomputed against their declared evidence inputs (10 matched, 4 undeclarable, 0 mismatched). FMSM (MIT) attribution for the adapted audit rubrics/prompts verified present in all 10 adapting files plus repo LICENSE (MIT); the material is attributed as rewritten-not-copied, so no license-notice gap was found.

Exclusions:

  • governance modality artifacts were unavailable this run; the department assessed from direct manifest/workflow/script reads and command verification instead of admitted governance evidence.
  • Invoke-Expression usage inside scripts/tests/test-repowise-provider-probe-classification.ps1:48 and scripts/tests/test-sentrux-failure-normalization.ps1:13 executes repo-local content only; code-level injection surfaces are handed to the security department.
  • install-code-intel-pipeline.ps1:931 documents CODE_INTEL_ANTHROPIC_BASE_URL with example value https://api.minimaxi.com/anthropic; model-provider routing, key handling, and provider cost/abuse are handed to the ai-safety department.
  • Advisory-database CVE lookups were not performed, per the department prompt's focus rules (counts not verifiable from the tree).
  • target/, node_modules/, .git/, cache/, and artifacts/ were intentionally excluded from scans as build output or generated content.
  • prototypes/session-observability is publish=false with its own clean 12-package lockfile; it is not built by any workflow, so its build path was not audited beyond lockfile parsing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions