Skip to content

'SBOM per release' is only an expiring workflow artifact: never attached to a GitHub Release, though docs tell auditors to gh release download it (CR-A16-004) #114

Open

Description

@cemililik

Wave 0 · theme D0-8: Packaging and delivery promises · highest severity High · 22 finding(s) · effort medium

Summary

An auditor follows the ISO/SOC2 deployer guide and runs gh release download v0.11.0 --pattern 'sbom-*'. Nothing is downloaded (there is no release, or a manually made one has no assets). If they instead get a run artifact, it lists pytest and ruff as product components and has no license data for the A.5.32 control it is mapped to.

Primary finding

CR-A16-004 — 'SBOM per release' is only an expiring workflow artifact: never attached to a GitHub Release, though docs tell auditors to gh release download it

severity High · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN OPS-02/03/04/05/06/15/16/19 (S12; split not public) — docs/roadmap/phase-16-trust-surface-hardening.md:667-668

What happens. publish.yml:104-111 writes sbom--py.json from tools/generate_sbom.py and uploads it only with actions/upload-artifact (retention-bound); no workflow creates a release or holds contents: write. supply_chain_security.md:41-42,55-56,65 (and TR), iso_soc2_deployer_guide.md:201-202, the user manual and sop_change_management.md promise release-page attachment and tag reproducibility. The SBOM is also built from the test runner's pip list (pytest, extras, no licences).

Where.

Evidence.

  • Running generate_sbom.py in the review venv produced 90 components, including bandit, mypy, pip, pytest, pytest-cov, ruff and setuptools; the top-level keys are only bomFormat/components/metadata/serialNumber/specVersion/version (no dependencies). No component has a licenses field. rg 'gh release|softprops|action-gh-release' .github finds nothing.
  • rg over .github/ for 'gh release|create-release|action-gh-release|contents: write' finds nothing; publish.yml's only SBOM sink is upload-artifact@v5.
  • Static reading. publish.yml has no gh release or release action. The SBOM step runs after the pytest and extras installs in the same job.

Recommended fix. Add a release job (contents: write) that creates the GitHub Release and uploads the SBOMs. Generate the SBOM from a clean venv holding only the wheel and its runtime dependencies. Add license data from importlib.metadata, or drop the license and reproducibility claims from the docs.

Other findings with the same root cause

These close with the same fix; update them in the same pull request.

  • CR-A16-027 (code, Medium) — SBOM records the unpinned runner environment (pip list), so it is not reproducible from the tag and does not describe the released package's dependency closure
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. An auditor checks out tag vX.Y.Z weeks after the release, installs it and runs python3 tools/generate_sbom.py. torch, transformers, trl, pip, setuptools and pytest have since released new versions inside the declared ranges, so the diff is non-empty and SOP 4.3 would label it 'dependency-resolution drift' although nothing about the release changed. A real tampered or substituted dependency in the original run cannot be told apart from this background change.
    Where. tools/generate_sbom.py:33-62 (confirmed), tools/generate_sbom.py:100, tools/generate_sbom.py:141-145, .github/workflows/publish.yml:72-85 (pip upgrade, unlocked wheel install, Linux-only extras, pytest install)
    Recommended fix. Either make the reproducibility claim true or withdraw it. To make it true: emit the SBOM from a pinned, recorded resolution (e.g. pip freeze of a constraints file committed or attached with the release, or install with --constraint and record it). Scope it to the installed wheel's dependency closure (exclude pytest, pytest-cov, pip and setuptools, or install them in a separate environment),…

  • CR-A16-028 (code, Medium) — SBOM carries no licence data although ISO A.5.32 / SOC2 CC9.2 / SoA cite 'License extraction in SBOM'
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. An ISO 27001 auditor asks for A.5.32 evidence. The deployer points to the SoA row and downloads the SBOM, which holds no licence information. The control is recorded as covered when it is not.
    Where. tools/generate_sbom.py:77-88 (_component: type/name/version/purl/bom-ref only), docs/reference/iso27001_control_mapping.md:56, docs/reference/iso27001_control_mapping-tr.md:55, docs/qms/statement_of_applicability.md:61
    Recommended fix. Either emit licenses per component (importlib.metadata License-Expression / License / classifiers, mapped to SPDX ids or name) or remove 'License extraction in SBOM' from both mapping documents and both SoA mirrors, and say that licence review is deployer-side.

  • TECH-T12-001 (technology, Now) — Supply-chain tooling, network dependencies and vulnerability scanning: SBOM distribution ('attached to the GitHub release') and SBOM content (publish.yml:104-111, tools/generate_sbom.py:77-133) (inco…
    severity Now · already tracked: KNOWN-OPEN OPS-02|OPS-03|OPS-04|OPS-05|OPS-06|OPS-15|OPS-16|OPS-19 docs/roadmap/phase-16-trust-surface-hardening.md:665-668
    Item. SBOM distribution ('attached to the GitHub release') and SBOM content (publish.yml:104-111, tools/generate_sbom.py:77-133) · verdict: incorrect-claim · urgency: Now
    State in ForgeLM. publish.yml:104-111 runs tools/generate_sbom.py inside the cross-os-tests matrix and stores each SBOM only as a workflow artifact (actions/upload-artifact@v5); nothing in .github/ creates a release or uploads release assets. The SBOM is a pip list inventory of the CI test environment (wheel, extras, pytest, pytest-cov, pip) with only type/name/version/purl/bom-ref per component: no hashes, lice…
    State of the ecosystem on 2026-10-01. GitHub API (retrieved 2026-10-01): releases v0.5.0 through v0.11.0 all have zero assets. GitHub Docs: workflow artifacts and logs are retained for 90 days by default before automatic deletion. Under the default, the SBOM artifacts of v0.5.0-v0.8.0 (published up to 2026-06-16) are already gone, v0.9.0 (2026-07-04) expires about 2026-10-02 and v0.10.0/v0.11.0 (2026-07-20/21) about 2026-10-18/19; th…
    Recommendation. Now: download the surviving sbom-* artifacts of v0.9.0-v0.11.0 and attach them to their releases (gh release upload), or raise artifact retention, before they expire. Then add a release-asset step to publish.yml (job-scoped contents: write, SHA-pinned action or gh CLI) so every tag attaches its SBOMs. Next release: generate the SBOM from a clean venv holding only the wheel and its runtime closure…
    Sources.

  • DOC-D1B03-002 (docs, High) — docs/qms/sop_change_management.md: false claim — An auditor can re-emit the SBOM on demand and diff against the artefact attached to the GitHub release.
    severity High · already tracked: KNOWN-OPEN OPS-02..19 (S12) — docs/roadmap/phase-16-trust-surface-hardening.md:667-668
    Where. docs/qms/sop_change_management.md:132-134 (Turkish mirror: docs/qms/sop_change_management-tr.md (section 4.3)) · type: false claim
    The document says. An auditor can re-emit the SBOM on demand and diff against the artefact attached to the GitHub release.
    What the code does. No SBOM is attached to a GitHub release; publish.yml only uploads workflow-run artefacts (see supply_chain_security.md finding).
    Code references. .github/workflows/publish.yml:104-111
    Proposed correction. Write: '... diff against the SBOM artefact of the corresponding publish workflow run (Actions artefacts, 90-day default retention); archive it in your own records.'
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D1B03-005 (docs, High) — docs/reference/supply_chain_security.md: false claim — The SBOM artefacts are downloadable from the GitHub release page; gh release download v<X.Y.Z> --pattern 'sbom-' fetches them; an auditor diffs…
    severity High · already tracked: KNOWN-OPEN OPS-02..19 (S12) — docs/roadmap/phase-16-trust-surface-hardening.md:667-668
    Where. docs/reference/supply_chain_security.md:41-42, 55-58, 62-66 (Turkish mirror: docs/reference/supply_chain_security-tr.md (Generation / Consuming the SBOM sections)) · type: false claim
    The document says. The SBOM artefacts are downloadable from the GitHub release page; gh release download v<X.Y.Z> --pattern 'sbom-
    ' fetches them; an auditor diffs against 'the artefact attached to the GitHub release'.
    What the code does. publish.yml only runs actions/upload-artifact for each SBOM (workflow-run artefacts). No job creates a GitHub release or uploads release assets, and the publish job only publishes to PyPI. 'gh release download --pattern sbom-' therefore finds nothing. Workflow artefacts expire (90 days by default). The external WS-C check in tech-verdicts T12 (item on SBOM distribution) records that releases v0.…
    Code references. .github/workflows/publish.yml:104-111, .github/workflows/publish.yml:113-136
    Proposed correction. Temporary text: 'The SBOMs are uploaded as artefacts of the publish workflow run (Actions tab), retained per the repository retention setting (90 days by default); they are not attached to the GitHub release.' Replace the gh release download example with 'gh run download --pattern sbom-
    '.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D2B03-006 (docs, High) — docs/design/iso27001_soc2_alignment.md: false claim — safe_post is 'TLS-only' / 'HTTPS-only'; 'TLS-only webhooks'; webhook discipline gives TLS.
    severity High
    Where. docs/design/iso27001_soc2_alignment.md:122,166,251,252,364 · type: false claim
    The document says. safe_post is 'TLS-only' / 'HTTPS-only'; 'TLS-only webhooks'; webhook discipline gives TLS.
    What the code does. Webhooks are posted with allow_insecure_http = not require_https, and webhook.require_https defaults to False, so http:// webhooks are sent by default with only a warning. Only the judge and synthetic-teacher paths always require HTTPS.
    Code references. forgelm/webhook.py:156-173, forgelm/config.py:1492, forgelm/_http.py:23-25, forgelm/_http.py:529-537
    Proposed correction. "safe_post: SSRF guard, no redirects, header masking; HTTPS is mandatory for judge/teacher calls and for webhooks only when webhook.require_https: true (the default allows http:// with a warning)." Apply this to A.5.14, A.8.20, A.8.21 and P6.1.

  • DOC-D2B03-007 (docs, High) — docs/design/iso27001_soc2_alignment.md: false claim — A.5.34: 'forgelm audit PII detection + masking'; A.8.11 (FL): 'forgelm audit regex + Presidio ML-NER masking'.
    severity High
    Where. docs/design/iso27001_soc2_alignment.md:186,242 · type: false claim
    The document says. A.5.34: 'forgelm audit PII detection + masking'; A.8.11 (FL): 'forgelm audit regex + Presidio ML-NER masking'.
    What the code does. forgelm audit only detects and reports. Its own messages tell users to mask with forgelm ingest --pii-mask / --secrets-mask. Masking (mask_pii, mask_secrets) exists only in ingestion, and only as regex. Presidio is used for detection only (--pii-ml), never for masking.
    Code references. forgelm/cli/subcommands/_audit.py:89, forgelm/cli/subcommands/_audit.py:146, forgelm/data_audit/_pii_ml.py, forgelm/ingestion.py
    Proposed correction. A.8.11: "forgelm ingest --pii-mask / --secrets-mask / --all-mask (regex masking before chunks are written); forgelm audit (regex + optional Presidio ML-NER) detects and reports only". Apply the same correction to A.5.34.

  • DOC-D2B03-012 (docs, High) — docs/guides/iso_soc2_deployer_guide.md: broken example — 'Download the SBOM artefact from the GitHub release page': gh release download v0.5.5 --pattern 'sbom-*'.
    severity High · already tracked: KNOWN-OPEN OPS-02..19 (S12) — docs/roadmap/phase-16-trust-surface-hardening.md:667-668
    Where. docs/guides/iso_soc2_deployer_guide.md:200-202 (Turkish mirror: docs/guides/iso_soc2_deployer_guide-tr.md:201-202) · type: broken example
    The document says. 'Download the SBOM artefact from the GitHub release page': gh release download v0.5.5 --pattern 'sbom-*'.
    What the code does. publish.yml uploads the SBOMs with actions/upload-artifact as workflow-run artifacts, and no job attaches them to a GitHub Release (the publish job only pushes to PyPI). gh release download therefore finds nothing, and workflow artifacts are also subject to Actions retention. v0.5.5 is stale; pyproject is 0.11.1rc1.
    Code references. .github/workflows/publish.yml:104-111, .github/workflows/publish.yml:113-136, pyproject.toml:19
    Proposed correction. "# Download the SBOM artifacts from the tag's Release workflow run (Actions artifacts; retention-limited).\ngh run download --pattern 'sbom-*'" (or regenerate with python3 tools/generate_sbom.py), and archive them yourself for each version you deploy.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D3B02-019 (docs, High) — docs/usermanuals/en/operations/iso-soc2-deployer.md: false claim — A CycloneDX 1.5 SBOM is "emitted per release" and fetched with gh release download "$(gh release view --json tagName -q .tagName)"… severity **High** · already tracked: KNOWN-OPEN OPS-02|OPS-03|OPS-04|OPS-05|OPS-06|OPS-15|OPS-16|OPS-19 docs/roadmap/phase-16-trust-surface-hardening.md:667-668 **Where.** [docs/usermanuals/en/operations/iso-soc2-deployer.md:17](https://github.com/HodeTech/ForgeLM/blob/cab2947/docs/usermanuals/en/operations/iso-soc2-deployer.md#L17),26 (Turkish mirror: [docs/usermanuals/tr/operations/iso-soc2-deployer.md:17](https://github.com/HodeTech/ForgeLM/blob/cab2947/docs/usermanuals/tr/operations/iso-soc2-deployer.md#L17),26) · type: false claim **The document says.** A CycloneDX 1.5 SBOM is "emitted per release" and fetched with gh release download "$(gh release view --json tagName -q .tagName)" --pattern 'sbom-'. **What the code does.** publish.yml generates the SBOMs but uploads them only as workflow-run artifacts (actions/upload-artifact). No workflow creates a GitHub Release or uploads release assets, so gh release download ... --pattern 'sbom-' finds nothing. **Code references.** [.github/workflows/publish.yml:104-111](https://github.com/HodeTech/ForgeLM/blob/cab2947/.github/workflows/publish.yml#L104-L111), [.github/workflows/publish.yml:113-136](https://github.com/HodeTech/ForgeLM/blob/cab2947/.github/workflows/publish.yml#L113-L136) **Proposed correction.** Code fix (see rationale). Interim doc: "SBOMs are attached to the Releaseworkflow run for the tag (Actions → run → Artifactssbom--py); download with gh run download --pattern 'sbom-*'` before the artifact retention window expires."
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D3B02-020 (docs, High) — docs/usermanuals/en/operations/supply-chain.md: false claim — One CycloneDX JSON per (OS × Python) cell is "attached to the GitHub release", fetched with gh release download ... --pattern 'sbom-*'.
    severity High · already tracked: KNOWN-OPEN OPS-02|OPS-03|OPS-04|OPS-05|OPS-06|OPS-15|OPS-16|OPS-19 docs/roadmap/phase-16-trust-surface-hardening.md:667-668
    Where. docs/usermanuals/en/operations/supply-chain.md:14,22 (Turkish mirror: docs/usermanuals/tr/operations/supply-chain.md:14,22) · type: false claim
    The document says. One CycloneDX JSON per (OS × Python) cell is "attached to the GitHub release", fetched with gh release download ... --pattern 'sbom-*'.
    What the code does. The SBOMs are uploaded only as workflow-run artifacts. No step creates a release or uploads assets.
    Code references. .github/workflows/publish.yml:104-111
    Proposed correction. Code fix (see rationale). Interim: "SBOMs are uploaded as artifacts of the tag's Release workflow run; fetch them with gh run download <run-id> --pattern 'sbom-*'."
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-BLEGAL-105 (docs, Medium) — docs/reference/supply_chain_security.md: false claim — [IS-T12-03] 'diff against the artefact attached to the GitHub release' / gh release download v<X.Y.Z> --pattern 'sbom-*'
    severity Medium
    Where. docs/reference/supply_chain_security.md:55-56, :65 (Turkish mirror: docs/reference/supply_chain_security-tr.md:56, :65) · type: false claim
    The document says. [IS-T12-03] 'diff against the artefact attached to the GitHub release' / gh release download v<X.Y.Z> --pattern 'sbom-*'
    What the code does. Per the GitHub API on 2026-10-01, releases v0.5.0 through v0.11.0 all have zero assets. SBOMs exist only as workflow artifacts, which are retained for 90 days by default: v0.5.0-v0.8.0 SBOMs are already gone, and v0.9.0 expires about 2026-10-02. The repository's actual retention setting is not visible from the snapshot.
    Proposed correction. Attach the SBOMs to the GitHub release in publish.yml, or document the actual retrieval path (gh run download <run-id> --pattern 'sbom-*') and its retention limit.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-BLEGAL-106 (docs, Medium) — docs/reference/supply_chain_security.md: broken example — curl -X POST -H "X-Api-Key: $DT_KEY" -H "Content-Type: application/octet-stream" --data-binary @​sbom-ubuntu-latest-py3.11.json https://deptra…
    severity Medium
    Where. docs/reference/supply_chain_security.md:77-80 (Turkish mirror: docs/reference/supply_chain_security-tr.md:78-80) · type: broken example
    The document says. curl -X POST -H "X-Api-Key: $DT_KEY" -H "Content-Type: application/octet-stream" --data-binary @​sbom-ubuntu-latest-py3.11.json https://deptrack.example.com/api/v1/bom
    What the code does. Dependency-Track's BomResource (4.14.4, 5.1.1, master) exposes /v1/bom with exactly two upload handlers: PUT consuming application/json (BomSubmitRequest with a base64 'bom') and POST consuming multipart/form-data (parts project, or projectName+projectVersion(+autoCreate), and 'bom'). No application/octet-stream consumer exists. Separately, the preceding gh release download step currently finds…
    Proposed correction. curl -X POST -H "X-Api-Key: $DT_KEY" -F "project=" -F "bom=@​sbom-ubuntu-latest-py3.11.json" https://deptrack.example.com/api/v1/bom # or: PUT /api/v1/bom with JSON {"project": "", "bom": ""}
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D2B03-015 (docs, Medium) — docs/design/iso27001_soc2_alignment.md: wrong detail — The SBOM is 'uploaded as release artefact' / 'attached to every release'; FAQ Q8: 'uploaded as a release artefact for each (OS × Python-version)…
    severity Medium · already tracked: KNOWN-OPEN OPS-02..19 (S12) — docs/roadmap/phase-16-trust-surface-hardening.md:667-668
    Where. docs/design/iso27001_soc2_alignment.md:127,173,852-854 · type: wrong detail
    The document says. The SBOM is 'uploaded as release artefact' / 'attached to every release'; FAQ Q8: 'uploaded as a release artefact for each (OS × Python-version) matrix cell'.
    What the code does. The SBOMs are uploaded with actions/upload-artifact as workflow-run artifacts, which are retention-limited. No step attaches them to the GitHub Release. §12 L686 of this same document lists the release upload as deferred.
    Code references. .github/workflows/publish.yml:104-111, .github/workflows/publish.yml:113-136
    Proposed correction. "CycloneDX 1.5 JSON per (OS, Python) cell, uploaded as a workflow-run artifact of the tag's Release workflow (not attached to the GitHub Release page)."

  • DOC-D2B03-018 (docs, Medium) — docs/design/iso27001_soc2_alignment.md: misleading — audit_log.jsonl is 'forensic-grade (HMAC + hash chain + manifest sidecar)'; FAQ Q7: 'audit log is HMAC-chained and append-only'.
    severity Medium
    Where. docs/design/iso27001_soc2_alignment.md:180,185,246,302,846 · type: misleading
    The document says. audit_log.jsonl is 'forensic-grade (HMAC + hash chain + manifest sidecar)'; FAQ Q7: 'audit log is HMAC-chained and append-only'.
    What the code does. HMAC is written only when FORGELM_AUDIT_SECRET is set. Without it the log is unkeyed, and an edit to the last line keeps the chain self-consistent.
    Code references. forgelm/compliance.py:230-260, forgelm/compliance.py:575-581, README.md:120
    Proposed correction. Qualify each mention: "... hash chain + genesis manifest; HMAC-authenticated when FORGELM_AUDIT_SECRET is set (required for adversarial tamper-evidence)".

  • DOC-D2B03-019 (docs, Medium) — docs/design/iso27001_soc2_alignment.md: misleading — A.5.29: 'auto_revert flips to baseline model on safety regression'; A1.2: 'auto_revert swap-back to baseline'; §10: auto_revert as the in-pipe…
    severity Medium · already tracked: KNOWN-OPEN DOCS-03/DOCS-04 (S15 auto-revert) — docs/roadmap/phase-16-trust-surface-hardening.md:760-765
    Where. docs/design/iso27001_soc2_alignment.md:181,333,631 · type: misleading
    The document says. A.5.29: 'auto_revert flips to baseline model on safety regression'; A1.2: 'auto_revert swap-back to baseline'; §10: auto_revert as the in-pipeline rollback procedure.
    What the code does. auto_revert deletes the saved model directory, emits model.reverted and exits 3. There is no swap or flip to a baseline, and it is withheld when the safety evaluation produced no evidence.
    Code references. forgelm/trainer.py:960-1007, forgelm/trainer.py:1458-1466
    Proposed correction. "evaluation.auto_revert deletes the regressed output (model.reverted, exit 3); restoring a previous model is deployer-side".

  • DOC-D2B03-020 (docs, Medium) — docs/design/iso27001_soc2_alignment.md: false claim — A.5.32: 'License extraction in SBOM; HF Hub model-card metadata recorded in manifest'; CC9.2: '... license extraction'.
    severity Medium
    Where. docs/design/iso27001_soc2_alignment.md:184,324 · type: false claim
    The document says. A.5.32: 'License extraction in SBOM; HF Hub model-card metadata recorded in manifest'; CC9.2: '... license extraction'.
    What the code does. SBOM components carry only type, name, version, purl and bom-ref; there are no licences. The manifest records dataset-builder version, description and download size (best-effort), not model-card or licence metadata.
    Code references. tools/generate_sbom.py:77-88, forgelm/compliance.py:846-872, forgelm/compliance.py:1513-1548
    Proposed correction. Remove 'license extraction' from A.5.32 and CC9.2 and state: "ForgeLM records no licence data; derive licences from the SBOM purls with your SCA tool."

  • DOC-D3B02-037 (docs, Medium) — docs/usermanuals/en/operations/iso-soc2-deployer.md: misleading — "two distinct operator IDs prove segregation of duties".
    severity Medium
    Where. docs/usermanuals/en/operations/iso-soc2-deployer.md:24 (Turkish mirror: docs/usermanuals/tr/operations/iso-soc2-deployer.md:24) · type: misleading
    The document says. "two distinct operator IDs prove segregation of duties".
    What the code does. operator and approver are self-asserted strings from the FORGELM_OPERATOR env var or the OS username. ForgeLM does not authenticate them.
    Code references. forgelm/cli/subcommands/_approve.py:121-182, forgelm/compliance.py:168-237
    Proposed correction. "two distinct operator IDs are evidence of segregation of duties when FORGELM_OPERATOR is injected by your IdP/CI (ForgeLM does not authenticate it)."
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D3B02-039 (docs, Medium) — docs/usermanuals/en/operations/supply-chain.md: misleading — pip-audit policy: HIGH/CRITICAL → exit 1; MEDIUM → ::warning::; LOW → silent. "Exit 1 means a HIGH/CRITICAL vulnerability."
    severity Medium
    Where. docs/usermanuals/en/operations/supply-chain.md:15,49 (Turkish mirror: docs/usermanuals/tr/operations/supply-chain.md:15,49) · type: misleading
    The document says. pip-audit policy: HIGH/CRITICAL → exit 1; MEDIUM → ::warning::; LOW → silent. "Exit 1 means a HIGH/CRITICAL vulnerability."
    What the code does. Any finding whose severity cannot be classified is UNKNOWN and fails closed (exit 1). pip-audit's JSON carries no severity, so in practice every finding is UNKNOWN and fails the gate, MEDIUM included.
    Code references. tools/check_pip_audit.py:11-24, tools/check_pip_audit.py:100-120
    Proposed correction. Add: "UNKNOWN (unclassifiable) → exit 1 — pip-audit's JSON omits severity, so in practice every unsuppressed finding fails the gate; fixable findings name the fix version, unfixable ones point at the --ignores workflow."
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D1B03-022 (docs, Low) — docs/reference/supply_chain_security.md: wrong detail — Uses the OSV / GHSA databases (pip-audit's default).
    severity Low
    Where. docs/reference/supply_chain_security.md:111 (Turkish mirror: docs/reference/supply_chain_security-tr.md (pip-audit section)) · type: wrong detail
    The document says. Uses the OSV / GHSA databases (pip-audit's default).
    What the code does. The nightly runs plain 'pip-audit --format json' (nightly.yml:322-324) with no -s option. Per the WS-C check (tech-verdicts T12), pip-audit's --vulnerability-service defaults to pypi (choices osv, pypi, esms), which is the PyPI/PyPA advisory data, not OSV. The doc's own check_pip_audit.py comments also say the JSON carries no OSV severity.
    Code references. .github/workflows/nightly.yml:322-327
    Proposed correction. Write: 'Uses pip-audit's default vulnerability service (PyPI advisory data); pass -s osv to query OSV.'
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • DOC-D2B03-037 (docs, Low) — docs/design/iso27001_soc2_alignment.md: wrong detail — A.5.17 / A.8.5: 'safe_post rejects auth headers in non-HTTPS requests'.
    severity Low
    Where. docs/design/iso27001_soc2_alignment.md:169,236 · type: wrong detail
    The document says. A.5.17 / A.8.5: 'safe_post rejects auth headers in non-HTTPS requests'.
    What the code does. safe_post never inspects headers. It refuses http:// unless the caller opts in (only the webhook path does, by default); judge and teacher calls never opt in.
    Code references. forgelm/_http.py:23-25, forgelm/_http.py:503-537
    Proposed correction. "safe_post refuses http:// for bearer-token callers (judge, synthetic teacher); webhooks only when require_https: true".

  • DOC-D2B03-038 (docs, Low) — docs/design/iso27001_soc2_alignment.md: wrong detail — A.5.25 / CC7.3: 'data.erasure_failed, pipeline.failed, audit.classifier_load_failed events with error_class+error_message'.
    severity Low
    Where. docs/design/iso27001_soc2_alignment.md:177,319 · type: wrong detail
    The document says. A.5.25 / CC7.3: 'data.erasure_failed, pipeline.failed, audit.classifier_load_failed events with error_class+error_message'.
    What the code does. Only data.erasure_failed carries error_class and error_message. pipeline.failed carries error; audit.classifier_load_failed carries classifier and reason.
    Code references. forgelm/cli/subcommands/_purge.py:560, forgelm/cli/subcommands/_purge.py:651, forgelm/trainer.py:1702, forgelm/safety/_classifier.py:190-209
    Proposed correction. State the per-event payload keys (error_class/error_message; error; classifier/reason).

Acceptance criteria

  • A regression test reproduces the failure scenario and asserts the corrected behaviour.
  • Documentation listed above is corrected in English and in its Turkish mirror in the same PR.
  • Every finding in this issue is resolved or explicitly closed with a reason.
  • The release note names the contract this fix restores.

Found by the October 2026 ForgeLM code and documentation review against commit cab2947. Code links point at that commit; line numbers may have moved since. Finding IDs (CR-…, DOC-…, TECH-…) are stable references for this backlog.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: packaging-ciCode area: packaging-cibacklog: knownAlready tracked in docs/roadmapbugSomething isn't workingdependenciesDependency or ecosystem currencydocumentationImprovements or additions to documentationsecuritySecurity-relevantseverity: highWrong behaviour or a false claim users rely onsource: review-2026-10Found by the October 2026 code and documentation reviewurgency: nowTechnology currency: act nowwave: 0Roadmap wave 0: immediate (patch release)

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions