Skip to content

Hold every secret-store reference to the one manifest that declares it - #245

Merged
stxkxs merged 7 commits into
mainfrom
hold-the-secret-store-name-in-one-place
Sep 5, 2026
Merged

Hold every secret-store reference to the one manifest that declares it#245
stxkxs merged 7 commits into
mainfrom
hold-the-secret-store-name-in-one-place

Conversation

@stxkxs

@stxkxs stxkxs commented Sep 5, 2026

Copy link
Copy Markdown
Member

Closes #203.

Gated, not derived — and which one matters

The name cannot be derived into the manifests. The consumers live in four kustomize roots plus a Helm chart, no single root spans them, and kustomize has no substitution that reaches across. So the answer is: one manifest owns the name, a gate holds everything else to it, and the same name is published for the repositories this one cannot reach.

The population is ten, not five

The issue counted five chart defaults across four other repos. This repository restates the name ten more times:

where count
addons/bootstrap/secret-stores/cluster-secret-store.yaml — the declaration 1
secretStoreRef in plain manifests 4
secretStoreRef in Go-template chart source (catalog/druid) 4
applicationsets/secret-stores.yaml kustomize patch target 1

"Every eks-gitops manifest is already correct" was true, and was not the property. Correct today, compared by nothing.

The reference that is worse than an ExternalSecret

An ExternalSecret naming a missing store at least records SecretSyncedError on its own status. The ApplicationSet patch target records nothing:

$ kustomize build .        # target names a store that does not exist
exit=0
    aws:
      region: us-west-2    # the base value, unpatched

That patch rewrites the store's AWS region per cluster from the cluster Secret's region label. Unmatched, every cluster silently keeps us-west-2 and looks its secrets up in the wrong region — green render, green kubeconform, nothing anywhere reporting a failure.

scripts/check-secret-store-refs.py

Nothing in it is a list. It reads the declaration and requires exactly one cluster-wide store, that every secretStoreRef names it, that every kustomize patch target with a store kind names it, and that this catalog's ExternalSecrets agree on one apiVersion.

Two readers, because half the corpus is chart source that raises on yaml.safe_load_all. A gate written against the parser alone reports a clean run over the four plain manifests and never sees the four in the chart. The chart reader locates the secretStoreRef block by its key and reads fields from inside it, so a name: belonging to the target or the metadata cannot be mistaken for the store.

contracts/secret-store.json

The published half — option 1 in the issue — for the four repos this seat cannot edit. Name, kind and apiVersion, generated by --write and compared against the tree on every run. A hand-edit that disagrees with the manifest fails here: a contract that has drifted is worse than none, because the consumers asserting against it pass.

The apiVersion is published beside the name because the issue's own comments established they go stale together. Five charts declared external-secrets.io/v1beta1 against a CRD that lists but does not serve it — that passes helm, kubeconform and chart lint, because the version really is on the CRD.

.gitignore caught the contract, and the rule was right

*secret*.json under "# Secrets and credentials" swallowed it. Not force-added: the rule already carries !*secret*-store*.yaml and !*secret*-store*.yml — this catalog's position that a store manifest is a reference into secrets rather than secret material. The contract is that same category, so the negation is extended to the third extension the other two already cover, with the reason at the line. gitleaks dir . finds nothing in it.

Proof

Eight defects planted, each caught and each naming the file:

planted verdict
a plain-manifest consumer typos the store 1, names grafana-token.yaml
the chart source typos it (the half that doesn't parse as YAML) 1, names externalsecret.yaml
the patch target stops matching 1, "kustomize does not treat an unmatched target as an error"
the store renamed, nothing follows 1, all 9 references
an ExternalSecret drifts to v1beta1 1, "pinning to a coin flip"
the contract hand-edited 1, prints published vs declared
the contract deleted 1
a second cluster-wide store declared 1, "has not decided"

Then the tests by mutation, __pycache__ cleared between each:

mutant killed by
chart source skipped instead of read as text test_every_reference_the_tree_holds_is_in_the_corpus, test_the_corpus_spans_both_readers
the patch-target check dropped test_a_patch_target_that_stops_matching_is_reported
the contract written but never compared test_a_contract_that_drifted_from_the_manifest_is_reported
several declared stores allowed test_two_cluster_stores_leave_the_contract_undecided
the version split not reported test_two_external_secret_versions_leave_nothing_to_publish
the helm reader takes any name: in the file 4 tests

The first is the load-bearing one, and test_the_corpus_spans_both_readers asserts the tree still exercises both readers so the text one cannot become dead code unnoticed.

Not closed, and adjacent

The patch-target no-op is general. Eleven named kustomize patch targets exist across the ApplicationSets; only the one naming a store is checked here. The other ten — Grafana/external, four ExternalSecrets, three GrafanaDatasources, EC2NodeClass/default, CronJob/grafana-token-rotator — each silently do nothing if the object they name is renamed. Same failure, same silence, wider than this issue.

The four repositories still hardcoding the name are not this seat's; the contract is what their CI can assert against, and the gate says so in its own scope statement.

Verification

ruff clean, mypy clean over 50 files, yamllint clean, gitleaks clean, 522 tests across 20 modules, coverage 45.4% against the 42% floor with 14 per-gate floors held, controls.py 19 controls, empty-corpus.py 32 probed gates, reverify-gates.sh 48/48 against the floor of 45, check-named-things.py 197 references resolve, task validate clean.

Gated, not derived, and the message says which because the two are different
promises. The name cannot be derived into the manifests: the consumers live in
four kustomize roots plus a Helm chart, no single root spans them, and kustomize
has no substitution that reaches across. So one manifest owns the name and a
gate holds everything else to it — and the same name is published for the
repositories this one cannot reach.

─── The population is ten, not five ───

The issue counted five chart defaults across four other repositories. This
repository restates the same name ten more times:

  1  the declaration, addons/bootstrap/secret-stores/cluster-secret-store.yaml
  4  secretStoreRef in plain manifests (dashboards x3, secret-stores-managed x1)
  4  secretStoreRef in Go-template chart source (catalog/druid)
  1  a kustomize patch target in applicationsets/secret-stores.yaml

"Every eks-gitops manifest is already correct" was true and was not the
property. Correct today, compared by nothing.

─── The reference that is worse than an ExternalSecret ───

An ExternalSecret naming a store that does not exist at least records
SecretSyncedError on its own status. The ApplicationSet patch target records
nothing: kustomize does not treat an unmatched target as an error, so the build
exits 0 and emits the unpatched base. That patch rewrites the store's AWS region
per cluster from the cluster Secret's `region` label. Unmatched, every cluster
silently keeps us-west-2 and looks its secrets up in the wrong region, with a
green render and a green kubeconform.

─── scripts/check-secret-store-refs.py ───

Nothing in it is a list. It reads the declaration and requires that exactly one
cluster-wide store exists, that every secretStoreRef in the tree names it, that
every kustomize patch target with a store kind names it, and that this catalog's
ExternalSecrets agree on one apiVersion.

Two readers, because half the corpus is chart source that raises on
`yaml.safe_load_all`. A gate written against the parser alone reports a clean run
over the four plain manifests and never sees the four in the chart. The chart
reader is structure-aware — it locates the `secretStoreRef` block by its key and
reads the fields from inside it, so a `name:` belonging to the target or the
metadata cannot be mistaken for the store.

─── contracts/secret-store.json ───

The published half, for the four repositories this seat cannot edit: the store's
name, kind and apiVersion, generated by `--write` and compared against the tree
on every run. A hand-edit that disagrees with the manifest fails here — a
contract that has drifted is worse than none, because the consumers asserting
against it pass.

The apiVersion is published beside the name because they go stale together. Five
charts declared external-secrets.io/v1beta1 against a CRD that lists but does not
serve it, and that passes helm, kubeconform and chart lint, because the version
really is on the CRD. Only a live API server rejects it.

─── .gitignore ───

`*secret*.json` under "# Secrets and credentials" swallowed the contract. Not
force-added: the rule already carries `!*secret*-store*.yaml` and
`!*secret*-store*.yml`, this catalog's position that a store manifest is a
reference into secrets rather than secret material. The contract is that same
category, so the negation is extended to the third extension the other two
already cover, with the reason at the line.

─── Registration ───

`task validate:secret-store-refs`, inside `task validate` because it is hermetic
and needs no tool. CI runs it beside the ExternalSecret key gate. controls.py
plants the typo one repository in this org actually shipped —
`aws-secretsmanager` against `aws-secrets-manager` — in the chart source, which
is the half a parser-only gate drops. reverify-gates.sh plants three: the store
renamed, the patch target unmatched, and the contract hand-edited away from the
manifest. 48 `run` lines, floor 45.

MAX_UNCOVERED_GATES 11 -> 10: this gate arrived with its own tests, so the count
holds at 10 across 26 rather than 25.
Comment thread scripts/check-secret-store-refs.py Dismissed
Comment thread scripts/check-secret-store-refs.py Fixed
Comment thread scripts/check-secret-store-refs.py Fixed
Comment thread scripts/check-secret-store-refs.py Fixed
Brings in the burn-rate budget gate. Three collisions.

─── scripts/tests/controls.py, scripts/tests/run.py EXPECTED ───

Additive on both sides — each added a mutator, a registry entry and a test
module at the same insertion point, and neither edited the other's. All kept.

─── scripts/tests/run.py MAX_UNCOVERED_GATES ───

Both sides moved it 11 -> 10 off the same base, each because a gate arrived with
its own tests. The value agrees; only the prose differed. Main's wording is
kept: it says the same thing and carries the extra clause about an import
removed later giving that coverage back.

The merged tree holds 27 gate scripts and the count stays at 10, so the ratchet
is right rather than coincidentally satisfied.

─── scripts/tests/reverify-gates.sh ───

The planted defects are additive and all kept. The floor is not: both sides
raised MIN_CHECKS off the same base of 41, the burn-rate work to 44 for its
three `run` lines and this branch to 48 for its seven. The floor tracks the
harness's own size at three under the `run` count, which is the margin that lets
a check be retired without turning the harness red while still catching one that
stopped executing most of itself. The merged file carries 51 `run` lines, so the
floor is 48.

Taking either side verbatim resolves it downward: at 44, seven of the checks
this branch adds could stop running and the floor would still pass.
CodeQL raised four high `py/clear-text-logging-sensitive-data` on the
secret-store gate. Closed as a property rather than by tuning the four lines,
because one of the flows is real.

─── The flow that was real ───

`contracts/secret-store.json` is parsed as arbitrary JSON and the drift message
echoed the parsed object back with `json.dumps`. Whatever is in that file is
what the message repeats, and CI keeps the log. "It only ever holds a store
name" is the assumption a gate about secret plumbing should not be making about
a file named for secrets — and the same reasoning covers the manifests, whose
values also arrive from disk.

─── The property ───

A value read from a file reaches the output only once it has been verified to be
a Kubernetes object name, kind, or group version, checked against the API
server's own grammar with `fullmatch`. A string that matches is lowercase
alphanumerics with hyphens and dots, at most 253 characters, carrying no
separator a credential needs. One that does not match is reported by its field
rather than by its content.

`printable()` is where that happens, and the stand-in it substitutes is a
constant rather than a truncation: a prefix of a value that is not a name is
still whatever that value was.

─── The drift message ───

It no longer echoes the published side at all. It names the field that
disagrees and prints the DECLARED value — the tree's own, and the one the reader
has to act on anyway:

    clusterSecretStore.name: the manifest declares aws-secrets-manager;
                             the contract publishes something else

Strictly more useful than the pair it replaced, and there is no longer a path
from the contract file to the output.

─── Proof ───

An AWS-key-shaped string planted in the contract, in a consumer's
secretStoreRef, in an ApplicationSet patch target, and as an apiVersion. Each
run asserts the output does not contain it, and that the message still names
what the reader needs. Four mutants killed by those tests: `printable` returning
the value unchecked, matching with `search` instead of `fullmatch`, substituting
a truncation instead of the constant, and the drift path dumping the published
side again.
Comment thread scripts/check-secret-store-refs.py Dismissed
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

CI Results

Check Status
Zero-placeholder gate ✅ success
YAML Lint ✅ success
Dashboard gate (live grafana.com ids + AMG-saveable) ✅ success
Kyverno policy tests (+ verify-images contract) ✅ success
Fork-safety gate (no hardcoded org, blocking) ✅ success
Helm-render (every addon × every env) ✅ success
Rendered images carry no unacknowledged CRITICAL ✅ success
Policy-admission (Enforce-tier Kyverno vs the fleet) ✅ success
ApplicationSet schema + sync waves ✅ success
Appset render (Karpenter subnet selector) ✅ success
Secret scan (gitleaks) ✅ success
Render + assert + schema + misconfig (all environments) ✅ success
Renovate manager coverage ✅ success
Loki volume warns before cutoff ✅ success
Falco loads every rule set it installs ✅ success
Gate scripts lint and typecheck ✅ success
Policy validity (kustomize + kyverno) ✅ success
The gates themselves are tested ✅ success
Pods name ServiceAccounts that exist ✅ success
Catalog CRs admissible against the operator chart ✅ success
Catalog source reads its revision ✅ success

All checks passed.

The contract is generated. The question worth asking of it is "is this what the
generator produces", and comparing four parsed fields answers a weaker one: it
passes a file carrying an extra key, the same fields reordered, or a rewritten
comment. A consumer in another repository reads the file, not the four fields
this gate happens to look at.

So `rendered()` is the one canonical serialisation, `--write` emits it, and the
check compares the file against it byte for byte. Unparseable JSON stops being a
separate case — it is simply not what the generator emits.

The message reads nothing out of the file. What is in there is whatever somebody
put there, so a message quoting it back repeats it into a log CI keeps; the
reader has the file open and the remedy is `--write` and read the diff whatever
the difference is. That also removes the last path from the contract to the
output, which is two of the four CodeQL flows on this gate.

`printable()` stays: the manifests are still read from disk and their names are
still echoed, and a value is only echoed once it matches the API server's own
grammar. A test asserts the generator and the comparison cannot come apart —
`--write` followed by a check must pass, or every run is red with no way to fix
it.
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

CI Results

Check Status
Zero-placeholder gate ✅ success
YAML Lint ✅ success
Dashboard gate (live grafana.com ids + AMG-saveable) ✅ success
Kyverno policy tests (+ verify-images contract) ✅ success
Fork-safety gate (no hardcoded org, blocking) ✅ success
Helm-render (every addon × every env) ✅ success
Rendered images carry no unacknowledged CRITICAL ✅ success
Policy-admission (Enforce-tier Kyverno vs the fleet) ✅ success
ApplicationSet schema + sync waves ✅ success
Appset render (Karpenter subnet selector) ✅ success
Secret scan (gitleaks) ✅ success
Render + assert + schema + misconfig (all environments) ✅ success
Renovate manager coverage ✅ success
Loki volume warns before cutoff ✅ success
Falco loads every rule set it installs ✅ success
Gate scripts lint and typecheck ✅ success
Policy validity (kustomize + kyverno) ✅ success
The gates themselves are tested ✅ success
Pods name ServiceAccounts that exist ✅ success
Catalog CRs admissible against the operator chart ✅ success
Catalog source reads its revision ✅ success

All checks passed.

@stxkxs

stxkxs commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

Merged origin/main (a5461e6) and worked the CodeQL failure. 30 of 31 checks green; CodeQL is at 2 highs, down from 4.

The merge

Three collisions. controls.py and run.py's EXPECTED were additive — both kept. The two floors were not:

  • MAX_UNCOVERED_GATES: both sides moved 11 → 10 off the same base, each because a gate arrived with its own tests. The value agreed; only the prose differed, and main's wording is kept. The merged tree holds 27 gate scripts and the count stays at 10, so the ratchet is earned rather than coincidentally satisfied.
  • MIN_CHECKS: both raised off 41 — burn-rate to 44 for its three run lines, this branch to 48 for its seven. 51 lines merged, so 48. At 44, seven of the checks this branch adds could stop running and the floor would still pass.

Two of the four CodeQL alerts were real

The reflex reading is "false positive, that file only holds a store name". Two were not.

contracts/secret-store.json was parsed as arbitrary JSON and the drift message echoed the parsed object back with json.dumps. Whatever is in that file is what the message repeats, into a log CI keeps. "It only ever holds a store name" is exactly the assumption a gate about secret plumbing should not be making about a file named for secrets.

Closed in two moves, neither a tuning of the flagged lines:

printable() — a value read from a file reaches the output only once it has been verified to be a Kubernetes object name, kind or group version, fullmatched against the API server's own grammar. The stand-in it substitutes is a constant rather than a truncation: a prefix of a value that is not a name is still whatever that value was.

The contract is compared as bytes. rendered() is the one canonical serialisation, --write emits it, the check compares the file against it, and no message reads from the file at all. Strictly stronger than the field comparison it replaced — it catches an extra key, a reordering, a rewritten comment, all of which a consumer reads — and it removes the path from the contract to the output entirely. Unparseable JSON stops being a separate case.

Proven with an AWS-key-shaped string planted in the contract, in a consumer's secretStoreRef, in a patch target, and as an apiVersion; each run asserts the output does not contain it. Four mutants killed: printable returning the value unchecked, search instead of fullmatch, a truncation instead of the constant, and the drift path dumping the published side again.

The other two are this repository's own naming

They sit on the failure loop and the success line. Everything either prints is a repo-relative path, a count, or a value printable() has verified. CodeQL cannot see that a fullmatch against an anchored literal grammar constrains a value — on the matching branch the helper returns the string it was given — so the taint survives the sanitizer.

The taint source is the file names: addons/bootstrap/secret-stores/ and contracts/secret-store.json. A secret store is not secret material, and this repo has now had to say so twice in two different systems — .gitignore carries !*secret*-store*.yaml and !*secret*-store*.yml for exactly this reason, and this branch adds .json to that list. The ignore exception and the scanner false positive are the same misunderstanding.

I have not suppressed them. Anything further would be writing for the scanner rather than the reader — reconstructing verified strings character by character from a literal alphabet breaks the dataflow and explains nothing to anyone reading the code. Three options, and the call is yours: an in-source # codeql[...] suppression carrying the reason; a CodeQL config excluding scripts/ from this query; or leaving the two open with the reasoning recorded. Say which and I'll do it.

Verification at 5c306ea

ruff clean, mypy clean over 52 files, yamllint clean, gitleaks clean, 574 tests across 21 modules, coverage 47.3% against the 42% floor with 14 per-gate floors held, controls.py 20 controls, empty-corpus.py 33 probed gates, reverify-gates.sh 51/51 against the floor of 48, check-named-things.py 198 references resolve, task validate clean.

py/clear-text-logging-sensitive-data matches on the NAMES of the files this gate
reads — addons/bootstrap/secret-stores/ and contracts/secret-store.json — and
treats a path containing "secret-store" as secret material. A secret store is
the thing that holds secrets, not a secret: it is an address plus a set of
credentials-free provider settings, and the values printed at both sites are a
Kubernetes object name, a kind, a group version, a repository-relative path and
a count. Every one read from a file has been through printable(), which returns
it only on a fullmatch against the API server's own grammar and a fixed stand-in
otherwise.

This repository has now made the same distinction in two systems. The secrets
block in .gitignore carries !*secret*-store*.yaml and !*secret*-store*.yml, and
this branch added !*secret*-store*.json for the contract. Both are about a name
rather than a content, and the pattern will recur in whatever tool comes third.
That sentence is the durable part of this commit; the suppression is the
consequence.

─── Why at the site ───

A CodeQL config excluding scripts/ from this query removes the query from a
directory permanently. That is a list, and the worst kind: nothing inside
scripts/ would say it is exempt, and a file added next month inherits an
exemption nobody chose for it. A suppression at the site is visible to whoever
reads the line, travels with the code if the code moves, and dies when the line
does. Its scope is exactly what it covers.

Leaving the alerts open would be right if the reasoning were uncertain. It is
not — the taint source is filenames, which is a finding rather than a hunch, and
an open alert whose answer is known is a queue nobody drains.

─── Why not worked around in the code ───

A verified string can be rebuilt character by character out of a literal
alphabet, which defeats the dataflow and satisfies the analyser. That is worse
than a suppression: a suppression is legible as a decision and can be disagreed
with, and a defeated dataflow cannot even be seen. Two tests hold both halves —
one fails if a marker is left without the reason or a pointer to it, the other
if the code is contorted instead.

Note the first two of the four alerts on this gate were NOT this. Those were a
real flow — the contract parsed as arbitrary JSON and echoed back into a log —
and are closed by printable() and the byte comparison, not by these markers.
Comment thread scripts/check-secret-store-refs.py Fixed
Comment thread scripts/check-secret-store-refs.py Fixed
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

CI Results

Check Status
Zero-placeholder gate ✅ success
YAML Lint ✅ success
Dashboard gate (live grafana.com ids + AMG-saveable) ✅ success
Kyverno policy tests (+ verify-images contract) ✅ success
Fork-safety gate (no hardcoded org, blocking) ✅ success
Helm-render (every addon × every env) ✅ success
Rendered images carry no unacknowledged CRITICAL ❌ failure
Policy-admission (Enforce-tier Kyverno vs the fleet) ✅ success
ApplicationSet schema + sync waves ✅ success
Appset render (Karpenter subnet selector) ✅ success
Secret scan (gitleaks) ✅ success
Render + assert + schema + misconfig (all environments) ✅ success
Renovate manager coverage ✅ success
Loki volume warns before cutoff ✅ success
Falco loads every rule set it installs ✅ success
Gate scripts lint and typecheck ✅ success
Policy validity (kustomize + kyverno) ✅ success
The gates themselves are tested ✅ success
Pods name ServiceAccounts that exist ✅ success
Catalog CRs admissible against the operator chart ✅ success
Catalog source reads its revision ✅ success

One or more checks failed — see the job logs.

The markers were on the same line as the expression they cover. That is the
older `lgtm[...]` placement, and it does not suppress: `codeql[...]` is read from
the line BEFORE the alert and covers that one line only. Both markers moved, and
the placement rule is recorded next to the reason because it is load-bearing and
invisible when wrong.

The same-line form has a second cost worth stating. Annotating a line edits it,
which changes the alert's hash — the original closes as fixed and an identical
one opens beside it, so the alert count stays the same and the history says
something happened that did not.

A marker that does not suppress is worse than no marker: it reads as handled,
and the next person to look sees a decision that was never in force.
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

CI Results

Check Status
Zero-placeholder gate ✅ success
YAML Lint ✅ success
Dashboard gate (live grafana.com ids + AMG-saveable) ✅ success
Kyverno policy tests (+ verify-images contract) ✅ success
Fork-safety gate (no hardcoded org, blocking) ✅ success
Helm-render (every addon × every env) ✅ success
Rendered images carry no unacknowledged CRITICAL ✅ success
Policy-admission (Enforce-tier Kyverno vs the fleet) ✅ success
ApplicationSet schema + sync waves ✅ success
Appset render (Karpenter subnet selector) ✅ success
Secret scan (gitleaks) ✅ success
Render + assert + schema + misconfig (all environments) ✅ success
Renovate manager coverage ✅ success
Loki volume warns before cutoff ✅ success
Falco loads every rule set it installs ✅ success
Gate scripts lint and typecheck ✅ success
Policy validity (kustomize + kyverno) ✅ success
The gates themselves are tested ✅ success
Pods name ServiceAccounts that exist ✅ success
Catalog CRs admissible against the operator chart ✅ success
Catalog source reads its revision ✅ success

All checks passed.

@stxkxs

stxkxs commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

In-source suppression taken, with the reason. 30 of 31 checks green; CodeQL still red, and the reason is not the suppression's content — it is the mechanism.

What is at the site

Both markers carry the reasoning: the query matches on the names of the files this gate reads, and a secret store is the thing that holds secrets rather than a secret. The comment records that this repository has now made that same distinction in two systems — .gitignore already carried !*secret*-store*.yaml and !*secret*-store*.yml, this branch added .json — and that a config exclusion would be a list covering every file added to scripts/ afterwards, none of which chose it.

Two tests hold the shape rather than the text: test_every_suppression_carries_its_reason fails if a marker is left without the reason or a pointer to it; test_no_value_is_rebuilt_to_defeat_the_dataflow fails if the code is contorted instead. Both proven by mutation.

I had the placement wrong first

# codeql[rule-id] is read from the line before the alert and covers that one line. I put both markers on the same line — the older lgtm[...] form, which does not suppress. It has a second cost worth recording: annotating a line edits it, so the alert's hash changes, the original closes as fixed and an identical one opens beside it. The count is unchanged and the history says something happened that did not. Fixed in 7114994, with the placement rule written next to the reason.

And it still does not turn the check green

Corrected placement, re-run, both alerts still state: open, dismissed_reason: null.

CodeQL here is default setupstate: configured, query_suite: default, weekly. There is no workflow file to change. The mechanism works in two halves and this repo has one: the CodeQL CLI reads the marker and stamps the SARIF result suppressions.kind: InSource, and something then has to act on that stamp to move the alert. Under default setup nothing does — which is why advanced-security/dismiss-alerts exists as a separate Action whose entire job is dismissing code-scanning alerts from SARIF suppression data.

So in-source suppression is available here as a record and not as a mechanism. The marker is correct, legible, travels with the code, dies with the line, and will suppress for anyone running the CLI or a pipeline that consumes the SARIF. The GitHub check stays red regardless.

What is left is a decision, and it is not mine

  • Dismiss the two alerts via the API with the reason as the dismissal comment, so the alert state and the code say the same thing. Closest to what was asked — but it is closing an alert by hand, which you forbade on Scan the images the pins reference, and make a CRITICAL a decision #239, and I will not do it unasked on a security surface.
  • Convert CodeQL to advanced setup with a dismiss-alerts step, which makes in-source markers load-bearing for every future alert too. That is a change to the repo's security configuration, well outside one issue.
  • Leave it red with the reasoning in the source, which is where it now is.

Say which and I'll do it.

Not mine, and already cleared

One run also failed the image-vulnerability gate: quay.io/argoproj/argo-events:v1.9.11 could not be scanned, so it exited 2 rather than reporting a clean result over images it never read. That is the behaviour the gate exists to have. It passed on the next run.

Verification at 7114994

ruff clean, mypy clean over 52 files, yamllint clean, gitleaks clean, 576 tests across 21 modules, coverage 47.3% against the 42% floor with 14 per-gate floors held, controls.py 20 controls, empty-corpus.py 33 probed gates, reverify-gates.sh 51/51 against the floor of 48, task validate clean.

It does not. Code scanning runs here as GitHub default setup, which reads no
suppression comment for this query: the two alerts stayed open with the marker
correctly placed on the line above each expression, and they were closed by
dismissing them in the scanner's own database, which is not in this tree.

The comment previously said "Suppressed at the site rather than excluded in a
CodeQL config". That sentence was false, and prose asserting a mechanism the
tool does not have is the same defect class this gate exists to catch — a claim
nothing holds to the thing it describes.

It now says what happens: the marker records the decision, the decision takes
effect somewhere a reader of this file cannot see, and it is addressed to a
person rather than to the tool. It stays because removing it leaves the site
with no explanation at all, which is worse than an explanation the scanner
ignores.

The placement note stays too, as the placement the marker WOULD need — on the
line before the expression, covering that line only — since the same-line
`lgtm[...]` form also edits the line it annotates and churns the alert's hash.

─── scripts/tests/test_secret_store_refs.py ───

`test_every_marker_has_its_reason_above_it` replaces a thirty-line window with
an ordering: the reason must appear BEFORE the marker, because a reader meets
them in that order. How many lines separate them is not a property worth
pinning, and pinning it made the test fail when the reason grew.

`test_the_marker_does_not_claim_to_suppress` fails if the prose goes back to
claiming the finding is suppressed here.
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

CI Results

Check Status
Zero-placeholder gate ✅ success
YAML Lint ✅ success
Dashboard gate (live grafana.com ids + AMG-saveable) ✅ success
Kyverno policy tests (+ verify-images contract) ✅ success
Fork-safety gate (no hardcoded org, blocking) ✅ success
Helm-render (every addon × every env) ✅ success
Rendered images carry no unacknowledged CRITICAL ✅ success
Policy-admission (Enforce-tier Kyverno vs the fleet) ✅ success
ApplicationSet schema + sync waves ✅ success
Appset render (Karpenter subnet selector) ✅ success
Secret scan (gitleaks) ✅ success
Render + assert + schema + misconfig (all environments) ✅ success
Renovate manager coverage ✅ success
Loki volume warns before cutoff ✅ success
Falco loads every rule set it installs ✅ success
Gate scripts lint and typecheck ✅ success
Policy validity (kustomize + kyverno) ✅ success
The gates themselves are tested ✅ success
Pods name ServiceAccounts that exist ✅ success
Catalog CRs admissible against the operator chart ✅ success
Catalog source reads its revision ✅ success

All checks passed.

@stxkxs
stxkxs merged commit a57d05b into main Sep 5, 2026
31 checks passed
@stxkxs
stxkxs deleted the hold-the-secret-store-name-in-one-place branch September 5, 2026 08:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The ClusterSecretStore name is hardcoded in five charts and compared by nothing

2 participants