Repository navigation
'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
Description
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.
.github/workflows/publish.yml:120tools/generate_sbom.py:33-62tools/generate_sbom.py:77-88docs/reference/supply_chain_security.md:55-56docs/reference/supply_chain_security.md:41-42docs/reference/supply_chain_security.md:63-65docs/guides/iso_soc2_deployer_guide.md:201-202docs/qms/sop_change_management.md:133-134docs/reference/iso27001_control_mapping.md:56.github/workflows/publish.yml:113-136
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' .githubfinds 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 releaseor 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 · reachabilitydefault· intentcontract-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 runspython3 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 freezeof a constraints file committed or attached with the release, or install with--constraintand 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 · reachabilitydefault· intentcontract-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 emitlicensesper component (importlib.metadata License-Expression / License / classifiers, mapped to SPDX ids orname) 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 apip listinventory 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.- GitHub API: HodeTech/ForgeLM releases v0.5.0..v0.11.0 (assets [] for all) (2026-07-21)
- GitHub Docs: Managing GitHub Actions settings for a repository (artifacts retained 90 days by default) (undated (retrieved 2026-10-01))
-
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_postis '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_postis '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, andwebhook.require_httpsdefaults 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 whenwebhook.require_https: true(the default allowshttp://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 auditPII detection + masking'; A.8.11 (FL): 'forgelm auditregex + 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 auditPII detection + masking'; A.8.11 (FL): 'forgelm auditregex + Presidio ML-NER masking'.
What the code does.forgelm auditonly detects and reports. Its own messages tell users to mask withforgelm 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 downloadtherefore 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'sReleaseworkflow run (Actions artifacts; retention-limited).\ngh run download --pattern 'sbom-*'" (or regenerate withpython3 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 withgh 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, sogh 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 theReleaseworkflow run for the tag (Actions → run → Artifactssbom--py); download withgh 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 withgh 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'sReleaseworkflow run; fetch them withgh 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 precedinggh release downloadstep 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.jsonlis '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.jsonlis '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 whenFORGELM_AUDIT_SECRETis set (required for adversarial tamper-evidence)". -
DOC-D2B03-019 (docs, Medium) — docs/design/iso27001_soc2_alignment.md: misleading — A.5.29: '
auto_revertflips to baseline model on safety regression'; A1.2: 'auto_revertswap-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_revertflips to baseline model on safety regression'; A1.2: 'auto_revertswap-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_revertdeletes 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.operatorandapproverare 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 whenFORGELM_OPERATORis 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--ignoresworkflow."
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_postrejects 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_postrejects 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_postrefuseshttp://for bearer-token callers (judge, synthetic teacher); webhooks only whenrequire_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_failedevents witherror_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_failedevents witherror_class+error_message'.
What the code does. Only data.erasure_failed carries error_class and error_message. pipeline.failed carrieserror; audit.classifier_load_failed carriesclassifierandreason.
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
Labels
Type
Projects
- StatusShow more project fieldsTodo
Activity
docs: compliance and privacy wording, SBOM and Article 12/14, and the…