Skip to content

chore(security): pin deps and actions, encrypt cache, harden CI#9

Merged
gabrielspadon merged 2 commits into
mainfrom
chore/security-hardening
Jul 5, 2026
Merged

chore(security): pin deps and actions, encrypt cache, harden CI#9
gabrielspadon merged 2 commits into
mainfrom
chore/security-hardening

Conversation

@gabrielspadon

Copy link
Copy Markdown
Collaborator

Summary

Supply-chain and secrets hardening. No pipeline-logic changes.

Dependencies (reproducible + hash-pinned)

  • Add requirements.lock (runtime) and requirements-dev.lock (dev), both --generate-hashes, universal.
  • CI now installs pip install --require-hashes -r <lock> then pip install -e . --no-deps.
  • Add defusedxml and route arXiv/DBLP XML parsing through it (entity-expansion safe).

CI supply chain

  • SHA-pin actions/checkout, setup-python, cache, upload-artifact.
  • Raise coverage floor --cov-fail-under 55 -> 68 (measured 70.6% locally).

data-cache exposure (public repo)

  • The monthly loop force-pushes the API-response cache to the public data-cache branch. It now AES-256 encrypts the tarball before push and decrypts on restore, keyed by a new CACHE_ENCRYPTION_KEY secret. If the key is unset it skips cache push/restore cleanly (fresh start), never exposing raw responses.

Runaway guard

  • The non-convergence re-trigger loop is capped at 12 runs per calendar month.

New

  • .github/dependabot.yml (github-actions + pip), .github/CODEOWNERS gating .github/, locks, and pyproject.

Follow-up (separate, operational)

  • Set the CACHE_ENCRYPTION_KEY secret and delete the existing plaintext data-cache branch so no raw responses remain public (branch regenerates encrypted on next run).

Local gates: ruff clean, mypy clean, 816 passed, coverage 70.6%.

- deps: add hashed lockfiles (requirements.lock, requirements-dev.lock);
  CI installs via --require-hashes then editable --no-deps (reproducible)
- actions: SHA-pin checkout/setup-python/cache/upload-artifact
- data-cache: AES-256 encrypt the tarball before force-push to the public
  data-cache branch; decrypt on restore; skip cleanly if key unset
- monthly loop: cap runs per calendar month to stop runaway re-triggers
- xml: parse arXiv/DBLP responses via defusedxml (entity-expansion safe)
- tests: raise coverage floor 55 -> 68 (measured 70.6%)
- add Dependabot (actions + pip) and CODEOWNERS gating .github and locks
Copilot AI review requested due to automatic review settings July 5, 2026 14:15
uv --universal alone dropped tomli (mypy's python<3.11 dep); pin the resolution to py3.10 so --require-hashes finds every transitive requirement.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the project’s supply chain and CI by introducing hash-pinned Python lockfiles, SHA-pinning GitHub Actions, and encrypting the public data-cache branch artifact used by the monthly refresh workflow. It also improves XML parsing safety by routing arXiv/DBLP XML parsing through defusedxml.

Changes:

  • Add requirements.lock and requirements-dev.lock with hash-pinned, reproducible dependency sets and update CI to install with --require-hashes.
  • Harden XML parsing by using defusedxml for arXiv/DBLP parsing and add defusedxml to project dependencies.
  • SHA-pin GitHub Actions, encrypt/decrypt the monthly cache tarball before pushing/restoring from data-cache, and cap re-trigger runs per month.

Reviewed changes

Copilot reviewed 6 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
requirements.lock Adds a hash-pinned runtime lockfile for reproducible installs.
requirements-dev.lock Adds a hash-pinned dev/test lockfile for reproducible CI tooling and tests.
pyproject.toml Adds defusedxml as a runtime dependency.
citeforge/clients/search_apis.py Switches XML parsing to defusedxml for safer arXiv/DBLP handling.
.github/workflows/tests.yml SHA-pins actions; installs from lockfiles with --require-hashes; raises coverage floor.
.github/workflows/monthly-refresh.yml Encrypts/decrypts cached API responses for the public data-cache branch; adds monthly runaway cap.
.github/dependabot.yml Enables Dependabot updates for GitHub Actions and pip dependencies.
.github/CODEOWNERS Adds ownership rules for CI, lockfiles, and pyproject.toml.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 359 to 362
try:
root = ElementTree.fromstring(xml)
root = safe_xml_fromstring(xml)
except XML_PARSE_ERRORS:
return []
Comment on lines 773 to 776
try:
root = ElementTree.fromstring(xml)
root = safe_xml_fromstring(xml)
except XML_PARSE_ERRORS:
return []
@gabrielspadon gabrielspadon merged commit c2a6ad2 into main Jul 5, 2026
6 checks passed
@gabrielspadon gabrielspadon deleted the chore/security-hardening branch July 5, 2026 14:19
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.

2 participants