Initial public ModelForge release
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# Supply-chain release gate
|
||||
|
||||
## What is inventoried
|
||||
|
||||
```text
|
||||
Python packages read from the built images, not from a manifest
|
||||
Node packages read from the lock file, which is what `npm ci` installs
|
||||
base images pinned per component
|
||||
CI actions pinned by commit SHA
|
||||
runtime images pinned by digest
|
||||
```
|
||||
|
||||
Reading the Python inventory from the image rather than the manifest matters: a manifest states
|
||||
intent, an image states what shipped. A build that silently resolved a different transitive version
|
||||
is exactly the case an SBOM exists to catch.
|
||||
|
||||
## SBOM
|
||||
|
||||
`scripts/m16_sbom.py` produces CycloneDX 1.5 at `docs/security/sbom/modelforge-cyclonedx.json`.
|
||||
It lists components and versions with package URLs, not dependency contents: a release inventory
|
||||
must be readable and diffable, so no binaries are stored.
|
||||
|
||||
Each component carries the image it belongs to, so a finding can be traced to what actually ships
|
||||
it rather than to the repository as a whole.
|
||||
|
||||
## Image provenance
|
||||
|
||||
`docs/security/sbom/image-provenance.json` binds each built image to its image id, repository
|
||||
digest where one exists, creation timestamp and the exact source commit. Provenance is what makes
|
||||
the SBOM verifiable rather than merely present: without it, an inventory describes an image nobody
|
||||
can identify again.
|
||||
|
||||
## CVE triage
|
||||
|
||||
Findings are classified, and the classification is evidence rather than a label:
|
||||
|
||||
```text
|
||||
EXPLOITABLE reachable from an untrusted input in this deployment
|
||||
NOT_REACHABLE the vulnerable path is not executed by ModelForge
|
||||
DEV_ONLY present only in a development or test dependency
|
||||
FALSE_POSITIVE the advisory does not apply to this version or configuration
|
||||
ACCEPTED_TEMPORARY accepted with an owner, a reason and a review date
|
||||
MUST_FIX blocks the release
|
||||
```
|
||||
|
||||
"Zero CVEs" is never claimed without the scan output that supports it, and an aggregate count never
|
||||
replaces the per-finding record for a HIGH or CRITICAL.
|
||||
|
||||
## Dependency pinning
|
||||
|
||||
Base images are pinned to a major or minor tag so they continue to receive security patches on a
|
||||
compatible line; the runtime worker is pinned by digest because its CUDA and PyTorch versions are
|
||||
part of the reproducibility contract for a benchmark. CI actions are pinned by commit SHA.
|
||||
|
||||
No dependency is declared as `latest`. A lock file makes a build reproducible in practice, but a
|
||||
floating specifier means the next `npm install` can pull a different major version — or a
|
||||
compromised release — without anyone deciding to.
|
||||
|
||||
## Container posture
|
||||
|
||||
```text
|
||||
non-root api, node-agent, web; postgres and redis drop to their own users
|
||||
no privileged mode none
|
||||
no Docker socket none
|
||||
capabilities dropped entirely on api, node-agent and web
|
||||
no-new-privileges api, node-agent, web
|
||||
read-only rootfs node-agent, web; api writes only to its mounted data volumes
|
||||
healthchecks api, postgres, redis, web
|
||||
```
|
||||
|
||||
## Network boundaries
|
||||
|
||||
```text
|
||||
PostgreSQL loopback only by default
|
||||
Redis loopback only by default
|
||||
console loopback only by default
|
||||
API published deliberately; every admin route is operator-authenticated
|
||||
runtime no public port; reached only through the gateway
|
||||
Node Agent outbound only; no inbound listener
|
||||
```
|
||||
|
||||
PostgreSQL and Redis defaulting to every interface was the most serious finding of the M16 review:
|
||||
the control-plane database, holding provenance, credential hashes and the audit trail, was reachable
|
||||
from the LAN behind a development password. Both now bind to loopback, overridable per deployment.
|
||||
|
||||
## Secrets
|
||||
|
||||
`gitleaks` runs over the full history, not the working tree. A finding is investigated rather than
|
||||
suppressed; the single allowlist entry names one exact string and says why it is not a credential.
|
||||
|
||||
Generated artefacts are checked too: logs, backup manifests, audit exports and the OpenAPI document
|
||||
must contain no operator key, encryption key, node credential or database password.
|
||||
|
||||
## Cryptography
|
||||
|
||||
Backup encryption is AES-256-GCM from `cryptography`. ModelForge designs no primitive of its own;
|
||||
`BackupCipher` is an adapter boundary so a KMS or HSM can replace the local key.
|
||||
|
||||
The M16 review verified, and static tests now enforce, that a fresh nonce is drawn inside the chunk
|
||||
loop rather than once per file, that each chunk is authenticated with associated data binding it to
|
||||
its key id and index, and that a decryption failure removes both the partial file and the
|
||||
destination so no readable plaintext survives a wrong key.
|
||||
Reference in New Issue
Block a user