docs: generate a page per reusable, with the check names it emits - #104
Merged
Conversation
18 reusables, 74 inputs, one of them documented. The README listed names and everything else meant reading the YAML. The part that has cost outages is narrower: no reusable stated the status-check names it emits. A required check is matched by name, and rust-ci alone emits 8 checks with 6 conditional on an input, so a repository configuring a ruleset had to guess. Guessing wrong produces a check nothing emits and blocks every pull request -- what happened in apt and klyradb. Generated from the workflows so it cannot drift, with docs-current.yml failing when the committed pages and the generator disagree. Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Jaro-c
added a commit
that referenced
this pull request
Jul 31, 2026
CodeQL has had a High alert open on `main` since #104 landed (py/clear-text-storage-sensitive-data, CWE-312): the page write in render-reusable-docs.py stores "sensitive data (secret) as clear text". The source is not the local's name — I renamed it twice and the alert did not move. It is `call.get("secrets")` itself. `SensitiveGetCall` in SensitiveDataSources.qll marks any `.get` whose first argument is a string literal matching the sensitive pattern, and `SensitiveSubscript` and `SensitiveAttributeAccess` cover `call["secrets"]` and `call.secrets`, so no spelling of an inline lookup avoids it. The literal has to reach the argument through `sensitiveLookupStringConst`, which is local flow by design. What the lookup returns here is the declaration of a reusable's `secrets:` block: names, required flags, descriptions, all of them already public in the workflow file this script parses. It never reads the environment, and today no reusable declares a secret at all, so the table never renders. So the query is describing a fetch this call does not perform, and moving the block names into CALL_BLOCKS makes the code state that: the generator documents the two blocks it knows about, and reads them by iterating that list. Byte-identical pages after regenerating. The secrets path is dead in this repository, so I exercised render() against a synthetic spec: a required secret, a null-valued declaration, a description with a pipe, the no-secrets case and a false-valued boolean input all render as before. Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
Jaro-c
added a commit
that referenced
this pull request
Jul 31, 2026
#107) ## Summary CodeQL alert #1 (High, `py/clear-text-storage-sensitive-data`, CWE-312/315/359) has been open on `main` since #104 landed: the page write in `scripts/render-reusable-docs.py` "stores sensitive data (secret) as clear text". The source is not the local variable's name. I renamed it twice and the alert did not move — the evidence is in this PR's first two CI runs. It is `call.get("secrets")` itself. From `SensitiveDataSources.qll`: ```ql class SensitiveGetCall extends SensitiveDataSource::Range, DataFlow::CallCfgNode { SensitiveGetCall() { this.getFunction().(DataFlow::AttrRef).getAttributeName() = "get" and this.getArg(0) = sensitiveLookupStringConst(classification) } ``` `SensitiveSubscript` and `SensitiveAttributeAccess` cover `call["secrets"]` and `call.secrets` the same way, so no spelling of an inline lookup avoids it. The way out is in `sensitiveLookupStringConst`, which requires the literal to reach the argument through **local** flow — deliberately, per its own note about cross-talk. What that lookup returns here is a reusable's `secrets:` **declaration**: names, required flags and descriptions, every one of them already public in the workflow file this script parses. Nothing reads the environment, and no reusable in this repository declares a secret today, so the table has never rendered. The query is describing a fetch this call does not perform. ## Changes - `CALL_BLOCKS` names the two `workflow_call:` blocks a page documents, and `render()` reads them by iterating it. The generator now states what it documents in one place instead of reaching for each block by literal at the point of use. ## Test plan - CodeQL: `pass`. Same run reddened twice on the two rename attempts, so the check is discriminating, not merely absent. - Regenerated the pages: byte-identical, `git status` clean apart from the script. - The secrets path is dead here, so I exercised `render()` directly: a required secret, a `None`-valued declaration, a description containing a pipe, the no-secrets case, and a `false` boolean input default all render exactly as before. - `docs match the workflows`, `actionlint`, `dco`: pass. Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
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
This repository publishes 18 reusable workflows with 74 inputs, and documented one of them. The README listed the names; everything else meant opening the YAML.
The part that has actually cost outages is narrower than "no docs": no reusable stated the status-check names it emits. A required status check is matched by name, so every repository configuring a ruleset had to guess the string — and
rust-cialone emits 8 checks, 6 of them conditional on an input. Guess wrong and you get a check nothing emits, which blocks every pull request in that repository until someone works out why. That is what happened inapt(a missing prefix) andklyradb(an invented one), and it is the same by-name matching that blocked every Dependabot pull request inaptearlier today.Changes
docs/reusables/, one page per reusable, generated from the workflow itself. Each page carries the call snippet, the emitted check names with the condition that decides whether each appears, the inputs with types and defaults, and secrets where there are any.A gate so it cannot rot.
docs-current.ymlregenerates and fails when the result differs from what is committed — the same shape as theproducts match the generatorcheck in the tap and the bucket. It also fails when a page is added or removed without the generator, which a plaingit diffwould miss.The generator does not invent prose: a page's summary is the workflow's own header comment. Four predate that convention and have a summary in the generator, so the source of every sentence is visible.
Test plan
rust-cireads as intended — all 8 checks, the 6 conditions spelled out,Test (${{ matrix.os }})marked as one check per matrix entry.false, not Python'sFalse, so a default can be pasted into YAML.workflow-lint's pinned shellcheck default without regenerating makes the comparison fail. Reverting it returns the tree to clean.Two things deliberately left out, both recorded rather than guessed: no page describes what a reusable does beyond its own header comment, and nothing here checks that a consumer's ruleset matches these names — that would need to read live settings, which
gh apirules out.