Add private-registry mount convention for source-scan dependency auth (#400) - #417
Merged
Merged
Conversation
Contributor
Author
|
Added the missing backend half: `MVN_ARGS` and `PIP_CONFIG_FILE` are now in `_CDXGEN_EXTRA_ALLOWLIST` (apps/backend/integrations/subprocess_env.py), plus a test exercising `scrubbed_env_for_cdxgen()` for all three variables. `NPM_CONFIG_USERCONFIG` needed no code change — it already passes through the existing `npm_config` prefix band, confirmed by the same test so a future narrowing of that band doesn't silently break it. The mount this PR adds is now functionally complete end to end: file lands in `/etc/trusca/registry/`, environment variable points cdxgen's subprocess at it, cdxgen forwards it to the underlying `mvn`/`npm`/`pip` invocation. |
…#400) Source scans have no way to authenticate cdxgen against a private Maven/npm/pip registry (container-image registry auth, ER3, is a separate feature). Operators can now drop settings.xml/.npmrc/pip.conf/ .netrc under REGISTRY_CONFIG_HOST_PATH (default ./secrets/registry), mounted read-only at a fixed /etc/trusca/registry on the scan-pipeline worker in both compose files, with a matching Helm worker.scan.extraEnv/ extraVolumes/extraVolumeMounts scoped to that Deployment alone. Maven's actual mechanism is cdxgen's own MVN_ARGS variable, not MAVEN_SETTINGS (which does not exist) - verified against @cyclonedx/cdxgen's source (lib/cli/index.js, lib/helpers/utils.js) and its own console guidance. NPM_CONFIG_USERCONFIG and PIP_CONFIG_FILE are npm's/pip's own documented env vars, inherited via normal subprocess env passthrough. This lands the mount convention, compose/Helm wiring, and docs (admin-guide/private-registries.md, EN+KO). The env vars still need to be added to integrations/_subprocess_env.py's cdxgen allowlist before they reach the cdxgen subprocess - out of scope for this PR (apps/backend is owned by backend-developer); see the PR description for the exact follow-up.
The private-registry mount this PR adds is inert without this: the files land on disk but cdxgen's subprocess never sees a pointer to them. Maven has no MAVEN_SETTINGS env var; cdxgen reads MVN_ARGS itself and appends it to the mvn invocation it builds (confirmed against @cyclonedx/cdxgen's source, not assumed). PIP_CONFIG_FILE is pip's own documented variable. NPM_CONFIG_USERCONFIG needed no change: it already passes through the existing npm_config_ prefix band. Both new entries are file paths, not credentials, so they are unaffected by _looks_like_credential.
The settings.xml guidance relied on a bare <server> entry, which Maven matches to a repository by <id> rather than <url> - a malicious pom.xml in the scanned project can declare a <repository> with a matching <id> and redirect the credential to an attacker URL. Add a <mirror mirrorOf="*"> example that pins resolution to one trusted URL instead, and document the risk under "Known limits". The .npmrc guidance did not show host-scoped credential lines. Add a //host/:_authToken= example and warn against unscoped _auth/_authToken, which npm would send to any registry a scanned project's package.json names via publishConfig.registry. Updates the admin guide (EN + KO) and the matching values.yaml comment.
…sages cdxgen shells into pip/npm/mvn for dependency resolution, and a failed request against a scheme://user:pass@host private-registry URL can echo that URL, credential included, into stderr. run_cdxgen copied stderr verbatim into CdxgenFailed, which the scan pipeline's generic failure handler stores as-is in scan.error_message, an API-exposed field any team member can read. Move the credential-shaped-substring scrubber already used for the live scan-log stream (tasks/_progress.py) into a shared integrations module so integrations/cdxgen.py can reuse it without a tasks -> integrations layering violation. Apply the same scrub to scancode's identical failure path and to the lockfile-prep failure log, which share the same shape.
haksungjang
force-pushed
the
feature/400-private-registry-auth-mount
branch
from
September 7, 2026 01:59
0236a83 to
326c845
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the infrastructure half of #400: a
/etc/trusca/registrymount convention (settings.xml / .npmrc / pip.conf / .netrc) so an operator can authenticatecdxgen's Maven/npm/pip dependency resolution against a private registry during a source scan. This is a separate feature from ER3's container-image registry credentials, which authenticate Trivy pulling an image.docker-compose.yml/docker-compose.dev.yml:REGISTRY_CONFIG_HOST_PATH(default./secrets/registry, same pattern asCOSIGN_KEYS_HOST_PATH) mounted read-only at the fixed path/etc/trusca/registryon the scan-pipeline worker only (worker-scan/celery-worker), plusMVN_ARGS/NPM_CONFIG_USERCONFIG/PIP_CONFIG_FILEpassthrough vars in thex-backend-envanchor. A commented-out second mount line covers.netrc, which has no pointer variable and must land at$HOME/.netrc.charts/trustedoss/: newworker.scan.extraEnv/extraVolumes/extraVolumeMounts, scoped to the scan Deployment alone (unlike the chart-wideenv.extraVolumesused for the private-CA example) since that is the only workload that runscdxgen.docs-site/docs/admin-guide/private-registries.md(+ KO mirror): full walkthrough, cross-linked from the Scans page's container-image "Private registries" section to disambiguate the two features.reference/env-variables.md(+ KO) documents the four new keys.CHANGELOG.mdentry,.gitignoreentries mirroring the cosign-key pattern.Maven mechanism (verified, not assumed)
The tracking issue's "Direction" note suggested a
MAVEN_SETTINGSvariable. That variable does not exist - Maven itself only reads~/.m2/settings.xmlor a-s/--settingsCLI flag, and Maven has no environment-variable form of that flag.I checked cdxgen's actual mechanism against its installed source (
@cyclonedx/cdxgen@12.2.0, the repo pins12.3.3):lib/cli/index.js:1526-1528andlib/helpers/utils.js:13210-1613: cdxgen readsprocess.env.MVN_ARGS, splits it on spaces, and appends it to themvnargument list it builds itself.lib/cli/index.js:1550-1554: when cdxgen finds asettings.xmlnext to apom.xmlit is scanning, its own console output says:"maven settings.xml found in ${basePath}. Please set the MVN_ARGS environment variable ... Example: MVN_ARGS='--settings ${settingsXml}'".lib/stages/pregen/envAudit.js:162:MVN_ARGSis also in cdxgen's own JVM-option-injection audit list, alongsideGRADLE_ARGS/JAVA_TOOL_OPTIONS, confirming it as a real, first-class cdxgen input.So
MVN_ARGS=--settings /etc/trusca/registry/settings.xmlis the actual mechanism, and it is cdxgen's own feature, not something this PR invents.NPM_CONFIG_USERCONFIG(npm) andPIP_CONFIG_FILE(pip) are each tool's own documented env vars - cdxgen does nothing special for them beyond normal subprocess env inheritance when it shells out tonpm install/pip install.Security notes (for security-reviewer)
/etc/trusca/registryis a literal indocker-compose.yml/ the Helm templates, never interpolated from a project setting, API field, or scan-trigger parameter - it cannot become an arbitrary-file-read via user input. OnlyREGISTRY_CONFIG_HOST_PATH(the host side) is operator-configurable, same as the existingCOSIGN_KEYS_HOST_PATHpattern.extraVolumeMountsexample are:ro/readOnly: true.worker.scan.*) is new and deliberately narrower than the existing chart-wideenv.extraVolumes(used for the private-CA example): a registry credential has no reason to be readable from backend/beat/worker-default, which never runcdxgen. Docker Compose mounts it onworker-scan/celery-workeronly, mirroring the existing cosign-key mount's scoping..netrcfootgun called out explicitly. Docker auto-creates a missing bind-mount source as an empty directory, which would silently shadow$HOME/.netrcfor every tool that reads it if an operator uncommented that line without creating the file first. Both compose files' comments and the doc page call this out before showing the line./workspaceand cdxgen only walks the cloned source tree there - but I have not audited the full archive/tar code path myself (out of scope:tasks/scan_source.pyis excluded from this PR). Flagging for security-reviewer to confirm.scan.error_messageleak risk (referenced F-1 precedent): I did not touch any error-message-construction code (out of scope,apps/backend/**), so there is nothing in this PR that could leak these paths into it, but the follow-up backend change (below) should re-check this when it lands.What still needs a backend-developer follow-up
integrations/_subprocess_env.pyis out of my scope as devops-engineer (apps/backend/**), so the mount and passthrough env vars in this PR do not yet reach thecdxgensubprocess -_CDXGEN_EXTRA_ALLOWLISTneeds three new entries added:I confirmed locally that
apps/backend/tests/unit/test_config_key_contract.py(the .env.example/reference/infrastructure parity oracle) passes cleanly both before and after this PR, so no test currently enforces that these three are forwarded - a new integration test exercisingscrubbed_env_for_cdxgen()with the allowlist entries in place (following the existing test patterns in that file) should land with the backend change.Refs #400. The offline install bundle (#400-4~6) is out of scope for this cycle.
Verification