Skip to content

[Wave 3] Medium — Module sweep: Packaging, container, CI/CD workflows & supply chain #566

Open

Description

@cemililik

Wave 3 · 13 finding(s) in 11 fix package(s). Fix these whenever a pull request touches this module ("clean the module you touch").

Findings

  • CR-A16-010 (code, Medium) — Documented 140-prompt safety library configs/safety_prompts/ is not shipped in the wheel or Docker image
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. A pip user copies the documented test_prompts: configs/safety_prompts/general_safety.jsonl; safety evaluation then cannot find its prompt file and fails at the safety gate, after training.
    Where. pyproject.toml:257-278, docs/guides/safety_compliance.md:107,118, docs/reference/configuration.md:211, docs/reference/architecture.md:94
    Recommended fix. Either package the sets (forgelm/safety_prompts/) and expose them by name, or change the docs to say they exist only in a source checkout and recommend forgelm safety-eval --default-probes / the packaged default_probes.jsonl for installed use.

  • ↳ same root cause: DOC-D1B01-039 (docs, Low) — docs/reference/configuration.md: misleading — test_prompts ... Built-in sets in configs/safety_prompts/
    severity Low
    Where. docs/reference/configuration.md:211 (Turkish mirror: docs/reference/configuration-tr.md) · type: misleading
    The document says. test_prompts ... Built-in sets in configs/safety_prompts/
    What the code does. The built-in sets are not available in an installed package; the default 'safety_prompts.jsonl' is a CWD-relative file that nothing ships.
    Code references. pyproject.toml:257-278
    Proposed correction. Same as above; also mention that the packaged default_probes.jsonl is addressed by --default-probes.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • ↳ same root cause: DOC-D2B02-068 (docs, Low) — docs/guides/safety_compliance.md: false claim — ForgeLM ships with curated adversarial prompt sets in configs/safety_prompts/
    severity Low
    Where. docs/guides/safety_compliance.md:107 (Turkish mirror: docs/guides/safety_compliance-tr.md) · type: false claim
    The document says. ForgeLM ships with curated adversarial prompt sets in configs/safety_prompts/
    What the code does. The six files are not in the wheel. After pip install forgelm only forgelm/safety_prompts/default_probes.jsonl (51 prompts) exists.
    Code references. pyproject.toml:268-270, forgelm/safety_prompts/default_probes.jsonl
    Proposed correction. Package the sets, or state that they exist only in a source checkout and point installed users to --default-probes / default_probes.jsonl.
    Turkish mirror. the Turkish page has the same error; fix both in the same PR

  • CR-A16-011 (code, Medium) — Gguf is used by verify-gguf but declared in no extra; the docs call it an 'optional extra'; a skipped metadata layer is not stated in the verdict
    severity Medium · reachability default · intent intended-but-harmful · confidence confirmed · independently confirmed · reproduced (plumbing only) · already tracked: KNOWN-OPEN TRUST-04 — docs/roadmap/phase-16-trust-surface-hardening.md:419-424 (S5 open)
    Failure scenario. A pipeline installs forgelm[export] and assumes metadata validation runs. verify-gguf prints OK and exits 0 with only the magic check and the sidecar, so a structurally corrupt GGUF with a matching or absent sidecar passes more weakly than the operator believes. Without a sidecar, a file with only the magic bytes 'GGUF' plus junk also exits 0 when gguf is absent.
    Where. pyproject.toml:126-131, forgelm/verify/_gguf.py:201-206, forgelm/verify/_gguf.py:240-248, docs/reference/verify_gguf_subcommand.md:158
    Recommended fix. Add an explicit extra (for example verify = ["gguf>=0.10,<1"]) or name it in the docs by its real install command, and append 'metadata check skipped (gguf package not installed)' to the success reason. Gain: the verdict is honest and reproducible; no runtime cost.

  • CR-A16-015 (code, Medium) — DeepSpeed presets hard-code optimizer (AdamW) and WarmupLR scheduler blocks, silently overriding ForgeLM's optim/GaLore and LR schedule
    severity Medium · reachability default · intent intended-but-harmful · confidence confirmed · independently confirmed · not reproduced (code trace)
    Failure scenario. A multi-GPU run reproduces a single-GPU config but trains at constant peak LR after warmup, or with plain AdamW instead of the configured GaLore. Nothing warns, and the run records claim the configured setup.
    Where. configs/deepspeed/zero2.json:13-29 (confirmed), configs/deepspeed/zero3.json:13-29 (confirmed), configs/deepspeed/zero3_offload.json:13-29 (confirmed), forgelm/trainer.py:544-562
    Recommended fix. Drop the optimizer and scheduler blocks from the presets so HF builds both from TrainingArguments (HF supports DS ZeRO with HF optimizer and scheduler). Otherwise, reject galore_enabled together with strategy: deepspeed and document the scheduler difference.

  • CR-A16-016 (code, Medium) — .dockerignore misses secrets, data, weights, audit logs and salts, so COPY . . bakes them into the image
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · not reproduced (code trace)
    Failure scenario. An operator follows the README/Dockerfile flow from the repo root, which holds .env (HF_TOKEN, FORGELM_AUDIT_SECRET, webhook URL), .hf_token, my_config.yaml with inline auth.hf_token, and ./data/*.jsonl with personal data. docker compose build or docker build . copies all of them into /app in the image. Pushing the image to a shared registry then leaks the HF token, the audit HMAC secret (which defeats audit-log tamper evidence) and the training data.
    Where. .dockerignore:1-68, Dockerfile:87-89, .gitignore, Dockerfile:12-15
    Recommended fix. Switch to an allowlist (* followed by !forgelm/ !pyproject.toml !README.md !LICENSE), or at least add the .gitignore secret and data entries.

  • CR-A16-018 (code, Medium) — Release matrix tests the git checkout, not the wheel it claims to validate
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN OPS-01 — docs/roadmap/phase-16-trust-surface-hardening.md:642-652,871-875 (remedy narrowed, C-12)
    Failure scenario. A future change drops a sub-package or package-data glob from the wheel, or a stale build/lib adds a shadow module. All 12 matrix cells pass because the tests import the source tree, and publish uploads a broken wheel. Users then hit ModuleNotFoundError or FileNotFoundError (e.g. forgelm safety-eval --default-probes).
    Where. .github/workflows/publish.yml:65-73, .github/workflows/publish.yml:87-102, tests/__init__.py, .github/workflows/nightly.yml:127-213
    Recommended fix. Run the matrix tests against the installed wheel: use --import-mode=importlib with the forgelm/ tree moved or removed, run from a copy of tests/ outside the checkout, or assert forgelm.__file__ is under site-packages at session start. Extend wheel-install-smoke to check every package-data group.

  • CR-A16-019 (code, Medium) — publish.yml publishes any pushed v* tag from any ref with only a pytest gate; no version/branch/lint/guard/bandit/pip-audit check, while docs and a test rationale claim audit gating
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · not reproduced (code trace) · already tracked: KNOWN-OPEN OPS-02..19 (S12) — docs/roadmap/phase-16-trust-surface-hardening.md:653-659,662-664
    Failure scenario. A maintainer tags a commit before bumping pyproject, or a collaborator pushes a v* tag from the development branch. The matrix passes (it tests the checkout, see the related finding), and PyPI permanently receives a mis-versioned or unreviewed release that never passed ci.yml's guards or a fresh pip-audit. PyPI versions cannot be re-uploaded.
    Where. .github/workflows/publish.yml:9-11, .github/workflows/publish.yml:113-136, tests/test_guard_wiring.py:80-84 (false claim at :83), docs/standards/release.md:309 (states pip-audit is NOT run by publish.yml; the false part is the 24h-audited assurance)
    Recommended fix. Add a first job asserting ${GITHUB_REF_NAME#v} == pyproject version and that the SHA is an ancestor of origin/main. Run (or require) the ci.yml validate/lint jobs and check_pip_audit against the tagged SHA. Correct the test_guard_wiring rationale, release.md:309 and the GPG sentence.

  • CR-A16-024 (code, Medium) — usermanuals-validate 'missing page' smoke gate can never fire: the workflow regex expects a quoted key, the builder emits an unquoted one
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. A section is renamed in _meta.yaml without moving its markdown. CI stays green and the live site shows placeholder pages.
    Where. .github/workflows/usermanuals-validate.yml:57, tools/build_usermanuals.py:316-328, tools/build_usermanuals.py:378-380 (confirmed), tools/build_usermanuals.py:317-327
    Recommended fix. Match missing:\s*true (unquoted), or have build_usermanuals.py exit non-zero, or emit a JSON manifest to check, plus a test that feeds a missing page through the workflow check.

  • CR-A16-029 (code, Medium) — Terms page says dependency licence compatibility 'is documented in pyproject.toml'; it is not, and the [ingestion] extra pulls in AGPL-3.0+ EbookLib, which is never disclosed
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Where. site/js/translations.js:4445, pyproject.toml:144, forgelm/ingestion.py:1313, pyproject.toml:23
    Recommended fix. Replace the pyproject claim with a real third-party licence table (or SBOM licence fields), and flag the AGPL EPUB dependency next to the [ingestion] extra and on the terms page.

  • CR-A16-030 (code, Medium) — The image does not include lm-eval by default, so the recipe's RUN forgelm cache-tasks exits 1; the docs say the image carries 'all extras' and never mention eval
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. docker build of airgap.Dockerfile on an image built with Dockerfile defaults stops at the cache-tasks RUN with exit 1, 'lm-eval is required for cache-tasks'.
    Where. Dockerfile:57-61 (ARG INSTALL_EVAL=false; [eval] installed only when it is set), Dockerfile:50-55, forgelm/cli/subcommands/_cache.py:405-415 (ImportError -> EXIT_CONFIG_ERROR=1), docs/usermanuals/en/operations/docker.md:30 ('latest' = 'Base + all extras (ingestion, scale, export, deepspeed)')
    Recommended fix. Make the recipe build its base with --build-arg INSTALL_EVAL=true (or add RUN pip install 'forgelm[eval]'). Correct the extras claims in docker.md:30 and installation.md:99 to match what the Dockerfile actually installs.

  • CR-A16-033 (code, Medium) — Docs promise SPDX conversion through a one-line cyclonedx-py call, but no cyclonedx-py version reads a CycloneDX BOM or writes SPDX
    severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
    Failure scenario. A deployer's procurement or auditor asks for an SPDX SBOM. The compliance team follows supply_chain_security.md and installs cyclonedx-bom. cyclonedx-py offers no way to feed it sbom-*.json or to emit SPDX; at best it regenerates a new CycloneDX BOM from whatever local venv the team has, which describes a different environment. The documented fallback behind decision D-22-02 is unavailable, and the team has to find cyclonedx-cli (.NET) on its own.
    Where. docs/reference/supply_chain_security.md:21-22, docs/reference/supply_chain_security-tr.md:21-22, docs/design/iso27001_soc2_alignment.md:431-434, docs/design/iso27001_soc2_alignment.md:787
    Recommended fix. Replace the recipe with the cyclonedx-cli convert command, or with spdx-tools/sbom-utility where verified. Keep the EN/TR mirrors aligned. Reword D-22-02's 'Reversible?' column so it does not rest on cyclonedx-py.

  • CR-A16-034 (code, Medium) — Documented Dependency-Track ingest command likely fails: raw octet-stream POST with no project identifier
    severity Medium · reachability default · intent contract-violation · confidence likely (static only) · independently confirmed · not reproduced (code trace)
    Failure scenario. An auditor follows the documented command against a Dependency-Track instance; the upload fails, and the SBOM is never ingested.
    Where. docs/reference/supply_chain_security.md:77-80, docs/reference/supply_chain_security.md:80, docs/reference/supply_chain_security-tr.md:77-80, docs/usermanuals/en/operations/supply-chain.md:31-34
    Recommended fix. Use the documented DT form: curl -X POST -H "X-Api-Key: $DT_KEY" -F autoCreate=true -F projectName=forgelm -F projectVersion=<X.Y.Z> -F bom=@​sbom.json .../api/v1/bom.

Done when

  • Every finding above is fixed or closed with a reason (tick it here).
  • Code fixes carry a test; doc fixes update the Turkish mirror in the same PR.

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.

Activity

  1. added
    bugSomething isn't working
    documentationImprovements or additions to documentation
    backlog: knownAlready tracked in docs/roadmap
    securitySecurity-relevant
    severity: mediumReal defect with limited reach or a workaround
    source: review-2026-10Found by the October 2026 code and documentation review
    wave: 3Roadmap wave 3: medium severity, module sweeps
    on Oct 9, 2026
  2. cemililik commented on Oct 9, 2026

    @cemililik
    CollaboratorAuthor

    A change on development that affects this issue — verified 2026-10-09 at e469e24.

    Repository settings changed on 2026-10-09 bear on CR-A16-019: only repository admins can create v* tags (the Release tags (v*) ruleset), and publish.yml's publish job waits for a maintainer to approve the pypi environment; release.md documents both (e469e24). A collaborator can no longer publish by pushing a tag, but the workflow still has no tag-vs-version, branch, guard or audit gate, so the finding stays open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: packaging-ciCode area: packaging-cibacklog: knownAlready tracked in docs/roadmapbugSomething isn't workingdocumentationImprovements or additions to documentationsecuritySecurity-relevantseverity: mediumReal defect with limited reach or a workaroundsource: review-2026-10Found by the October 2026 code and documentation reviewwave: 3Roadmap wave 3: medium severity, module sweeps

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions