Found by a scanner added alongside the release-guard environment fix. Priority P2.37 — severity medium, category ci.
Problem
The production GitHub environment contains zero secrets, but deploy.yml's deploy-production job declares environment: production and references at least ten of them:
REMOTE_HOST: ${{ secrets.HOST }}
REMOTE_USER: ${{ secrets.USER }}
REMOTE_PATH: ${{ secrets.PROJECT_PATH }}
ENV_ANTHROPIC_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ENV_OPENAI_KEY: ${{ secrets.OPENAI_API_KEY }}
ENV_AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
...
GitHub exposes environment secrets only to jobs declaring that environment, and production supplies none — so every one of these resolves to an empty string. For comparison, staging holds 20 secrets and the staging deploy works.
Verified against the live repo:
$ gh api repos/frankbria/codeframe/environments/production/secrets --jq .total_count
0
$ gh api repos/frankbria/codeframe/environments/staging/secrets --jq .total_count
20
What actually happens
The job fails early rather than silently deploying something broken — SSH_KEY is empty, so the SSH step cannot authenticate. That is the mitigating factor and why this is P2 rather than P1.
But if the SSH secrets were ever populated without the rest, the job would write a remote .env containing:
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
AUTH_SECRET=
An empty AUTH_SECRET is the dangerous one — that is the JWT signing key.
Decisions this needs
Not a code fix. One of:
- Populate
production with the same secret set as staging (different values), if production deploys are intended
- Delete the
deploy-production job and the production environment, if staging is the only deploy target — the repo currently ships a deploy path that has never worked
- Guard the job so it fails with an explicit "production is not configured" message rather than an SSH auth error
Acceptance criteria
Note
The scanner that found this asserts that any job using an environment secret declares an environment that actually provides it. deploy-production is currently exempted with a pointer to this issue rather than the check being weakened.
Problem
The
productionGitHub environment contains zero secrets, butdeploy.yml'sdeploy-productionjob declaresenvironment: productionand references at least ten of them:GitHub exposes environment secrets only to jobs declaring that environment, and
productionsupplies none — so every one of these resolves to an empty string. For comparison,stagingholds 20 secrets and the staging deploy works.Verified against the live repo:
What actually happens
The job fails early rather than silently deploying something broken —
SSH_KEYis empty, so the SSH step cannot authenticate. That is the mitigating factor and why this is P2 rather than P1.But if the SSH secrets were ever populated without the rest, the job would write a remote
.envcontaining:An empty
AUTH_SECRETis the dangerous one — that is the JWT signing key.Decisions this needs
Not a code fix. One of:
productionwith the same secret set asstaging(different values), if production deploys are intendeddeploy-productionjob and theproductionenvironment, if staging is the only deploy target — the repo currently ships a deploy path that has never workedAcceptance criteria
productionholds every secretdeploy-productionreferences, and one successful deploy is verifiedAUTH_SECRETis confirmed non-empty before any deploy writes the remote.envtests/test_workflow_lint_wiring_1122.py::TestEnvironmentSecretsAreReachableis removedNote
The scanner that found this asserts that any job using an environment secret declares an environment that actually provides it.
deploy-productionis currently exempted with a pointer to this issue rather than the check being weakened.