Summary
A set of CI/CD and config hardening gaps that individually are Medium/Low but share a theme (least privilege, pinning, and safe markup generation). Filed as one grouped issue with a checklist.
Location & Evidence
1. Missing least-privilege permissions: blocks — both workflows fall back to the repo/org default GITHUB_TOKEN scopes (potentially read/write all).
.github/workflows/contribution_approved.yml (no permissions: key; pushes to main + comments)
.github/workflows/update_readmes.yml (no permissions: key; pushes to main)
- Contrast
.github/workflows/auto_extract.yml:7-9 which does scope them.
2. Unpinned action and dependency versions (supply-chain / reproducibility)
- All workflows use floating tags:
actions/checkout@v4, actions/setup-python@v5, actions/github-script@v7.
.github/workflows/auto_extract.yml:33-35 installs unpinned deps ignoring requirements.txt:
- name: Install dependencies
run: pip install requests beautifulsoup4 openai
.github/scripts/requirements.txt uses >= (no upper bound / lockfile).
3. Incomplete HTML escaping in generated markup
.github/scripts/util.py:108-117 builds <a href="{url}">… with no escaping of " in url.
.github/scripts/generate_gallery.py:39-40 esc() escapes & < > but not ":
def esc(text):
return (str(text).replace("&", "&").replace("<", "<").replace(">", ">"))
A value containing " breaks out of the href/src/alt attribute.
4. Step output echoed into run: (same anti-pattern as the injection issue, lower risk here)
.github/workflows/update_readmes.yml:52-54:
run: |
echo "Error updating README: ${{ steps.update.outputs.error_message }}"
5. dangerouslyAllowSVG enabled without a Content-Security-Policy on next/image.
images: { remotePatterns: [ /* raw.githubusercontent... */ ], dangerouslyAllowSVG: true },
Impact
Broader-than-needed token scopes amplify the impact of any workflow injection; floating action tags allow a compromised upstream tag to run in CI with write access; incomplete escaping allows attribute-injection into the generated README (low real-world impact because GitHub sanitizes README HTML and the web app escapes via React, but it is still latent); dangerouslyAllowSVG without a CSP is a stored-XSS vector the day any user-influenced SVG can be served same-origin.
Recommended fix
Acceptance criteria
Summary
A set of CI/CD and config hardening gaps that individually are Medium/Low but share a theme (least privilege, pinning, and safe markup generation). Filed as one grouped issue with a checklist.
Location & Evidence
1. Missing least-privilege
permissions:blocks — both workflows fall back to the repo/org defaultGITHUB_TOKENscopes (potentially read/write all)..github/workflows/contribution_approved.yml(nopermissions:key; pushes tomain+ comments).github/workflows/update_readmes.yml(nopermissions:key; pushes tomain).github/workflows/auto_extract.yml:7-9which does scope them.2. Unpinned action and dependency versions (supply-chain / reproducibility)
actions/checkout@v4,actions/setup-python@v5,actions/github-script@v7..github/workflows/auto_extract.yml:33-35installs unpinned deps ignoringrequirements.txt:.github/scripts/requirements.txtuses>=(no upper bound / lockfile).3. Incomplete HTML escaping in generated markup
.github/scripts/util.py:108-117builds<a href="{url}">…with no escaping of"inurl..github/scripts/generate_gallery.py:39-40esc()escapes& < >but not":A value containing
"breaks out of thehref/src/altattribute.4. Step output echoed into
run:(same anti-pattern as the injection issue, lower risk here).github/workflows/update_readmes.yml:52-54:5.
dangerouslyAllowSVGenabled without a Content-Security-Policy on next/image.web/next.config.ts:7-16:Impact
Broader-than-needed token scopes amplify the impact of any workflow injection; floating action tags allow a compromised upstream tag to run in CI with write access; incomplete escaping allows attribute-injection into the generated README (low real-world impact because GitHub sanitizes README HTML and the web app escapes via React, but it is still latent);
dangerouslyAllowSVGwithout a CSP is a stored-XSS vector the day any user-influenced SVG can be served same-origin.Recommended fix
permissions:tocontribution_approved.yml(contents: write,issues: write) andupdate_readmes.yml(contents: write).requirements.txt(pip install -r .github/scripts/requirements.txt).esc()/format_linkto also escape"(and ideally').error_messageinupdate_readmes.ymltoenv:+$ERROR_MSG.images.contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;"andcontentDispositionType: "attachment"innext.config.ts.Acceptance criteria
permissions:block.href/src/altattributes are safe against a"in the value.next.config.tspairsdangerouslyAllowSVGwith a CSP.