Repository navigation
Conversation
…on Ubuntu 26.04 ubuntu-latest is now Ubuntu 26.04, and actions/setup-python has no 3.11.8 build for it, so every publish job of the 0.10.0-rc.6 release failed at "Python setup" before building anything. '3.11' takes the newest 3.11 the runner has, as tests.yml already does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ Deploy Preview for opal-docs canceled.
|
This repo had no dependency automation. It matters beyond OPAL itself, because OPAL is the upstream that downstream consumers inherit: permitio/pdp-v2 installs opal-client and opal-common, so the floors and caps declared here decide what a PDP image can resolve. Several CVE waivers in the PDP exist purely because a cap here forbids the fixed version. Ecosystems: pip (/), npm (/documentation), docker (/docker), github-actions (/) - weekly, Monday. Shape mirrors the PDP config so every repo behaves alike. cooldown is supply-chain defence rather than noise reduction (GitHub made a 3-day cooldown the platform default in July 2026); security updates are never delayed by it. Only semver-strict ecosystems support the semver-specific knobs, so pip/npm get semver-major-days and docker/github-actions get default-days only. A SCOPE LIMIT worth stating plainly, because it is easy to assume otherwise: the pip entry does NOT cover OPAL's library dependencies. Those live in packages/requires.txt and packages/*/requires.txt, and each setup.py loads them at runtime with a plain open() inside get_install_requires(). Dependabot's pip parser reads standard manifests and cannot resolve a dynamic file read, so those files are invisible to it. Bumping OPAL's actual caps therefore stays manual until they become a pyproject.toml - which is the highest-value follow-up here and deserves its own PR. What the entry does cover is still useful: two pins in the root requirements.txt already carry the comment "pinned by Snyk to avoid a vulnerability", so someone was doing this by hand. Real work the other entries will surface: docker/Dockerfile builds on python:3.10-bookworm and python:3.10-slim-bookworm, and Python 3.10 reaches END OF LIFE in October 2026. The PDP already moved off python:3.10 for that reason plus four CPython CVEs that only had 3.13 fixes. It also pulls alpine:latest twice - a floating tag with no reproducibility - and hand-pins openpolicyagent/opa:1.9.0-static and permitio/cedar-agent:0.2.1. The workflows mix actions/checkout@v2, @V3, @v4 and a bare commit SHA; actions/setup-python@v2, @v4, @v5; docker/build-push-action@v2 and @v6; docker/setup-buildx-action@v1 and @V3; docker/setup-qemu-action@v1 and @V3. The @v1/@v2 generations are years old and run on Node runtimes GitHub has already deprecated. The config ends with a DOWNSTREAM NOTE recording the three caps that force PDP waivers - starlette<1, ddtrace<4, and gitpython's >=3.1.32 floor - so the next person to touch requires.txt sees why they matter to a consumer. The gitpython one is the sharpest: 3.1.59 fixes CVE-2026-78676, a CRITICAL (CVSS 9.8) argument-injection RCE, and a floor of 3.1.32 lets any consumer resolve a vulnerable version. pdp-v2:0.9.14 shipped 3.1.57 and had to add its own floor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- The starlette <1 cap is gone (raised to >=1.3.1,<2 by #959), so drop it from the downstream note; ddtrace <4 and the gitpython 3.1.32 floor still stand. - The workflow action-version inventory now matches master (no checkout@v4; login-action@v2 added). - Drop an unverifiable claim about GitHub's default cooldown, the OPA version pin that #906 changes, and prefix-development on github-actions (no dev deps). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r, fix notes Review follow-ups: - Dependabot's pip fetcher reads any *.txt whose lines parse as requirements in a configured directory and its immediate subdirectories, so packages/requires.txt is covered from '/' and the per-package requires.txt files via 'directories'. The old comment said they were invisible to it. - Hold python minor/major tag bumps: the Dockerfile hard-codes the 3.10 site-packages path and opal-server caps python <3.13, so they'd only be red. - Add the test-only app-tests seed image; say which images Dependabot can see (FROM lines) and can't (OPA via skopeo ARG, EOPA via wget). - Correct the action inventory and the 'mirrors other configs' claim. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- pip: versioning-strategy increase-if-necessary. Requirement .txt files otherwise get `increase`, which raises every `>=` floor to the latest release; these are the published packages' ranges. - pip: a shared-majors group for aiohttp, click, gitpython, pyjwt and tenacity, which sit in more than one requires.txt, so a major update is one PR instead of one per directory. - Every entry: weekly on Monday, limit 5, 7-day cooldown (plus 14 days for semver-major on pip and npm), minor-and-patch and security groups, deps / deps-dev prefixes, the existing `dependencies` label. - docker: split /docker from the git-leak seed image so the python minor hold applies to /docker only, and say that the floating python:3.12 bases get no PRs while it is held. - pre-commit: new entry for the hook revs; black, isort and docformatter are ignored because a bump reformats the tree. - Drop the downstream note (ddtrace is >=4.8.2 and gitpython >=3.2.1 now), the python:3.10 notes, the hand-kept action inventory, tracker IDs and the references to non-public repositories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests.yml and docs-build.yml: - Every action pinned to a commit SHA with its version, one version per action across both files (checkout v7.0.1, setup-python v7.1.0, setup-node v7.1.0, setup-qemu v4.4.0, setup-buildx v4.4.1, build-push v7.4.0), each checkout with persist-credentials: false. - permissions: contents: read for the whole workflow. - A push to a PR cancels that PR's older runs; master and release branch runs are never cancelled. - Run steps use bash -euo pipefail; runners pinned to ubuntu-24.04. tests.yml only: - The pull_request trigger loses its base-branch filter, so a PR stacked on another branch gets the full CI. - redis service pinned to 8.10.3 (the floating tag resolves to it today); pip, flake8 and the 3.13 leg's pytest tools pinned exactly. - matrix values reach run: through env:, and the App Tests step quotes the key path (shellcheck SC2086, SC2155). - Drop the docker-compose 1.29.2 binary download from both E2E jobs: it was fetched unverified and nothing calls it (the steps and app-tests/run.sh use the `docker compose` plugin). - DOCKER_BUILD_RECORD_UPLOAD=false: build-push-action v6+ uploads a build record with the provenance, the same data that BUILDX_METADATA_PROVENANCE keeps out of the logs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each action is pinned to the commit its current major tag points at
(the commits the 0.10.0-rc.5 and rc.6 runs downloaded), so the same
code runs: checkout v2.8.0, setup-python v5.6.0, login-action v2.2.0,
setup-qemu v3.7.0, setup-buildx v3.12.0, build-push v6.19.2,
actions-upload-release-asset v1.7.5, gh-action-pypi-publish v1.14.3
(the release/v1 branch head).
- permissions: {} for the workflow; validate_release gets none,
build_and_test and publish_docker_images contents: read,
publish_python_packages contents: write for the release assets.
Nothing used id-token or pull-requests: the PyPI steps pass a token,
so the action never requests an OIDC token.
- publish_python_packages runs in the pypi environment (no protection
rules); the misplaced step-level env name/url is gone.
- Drop the unused TOKEN_GITHUB env and the unverified, unused
docker-compose 1.29.2 download.
- Release tag, prerelease flag and matrix values reach run: through
env:; quote $version_tag; cd ... || exit 1.
- Pin semver 3.1.0, packaging 26.3 and wheel 0.48.0, the versions the
rc.5 run installed.
- Image builds no longer read or write a registry cache, so layers from
an earlier image (such as a stale apt-get upgrade) cannot reach a
release.
- checkouts set persist-credentials: false.
- use-trusted-publishing stays ignored inline until PyPI trusted
publishing is set up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- .pre-commit-config.yaml: add actionlint v1.7.12, zizmor v1.31.1 and
gitleaks v8.30.1 (staged changes); every rev, the existing hooks
included, is now the tag's commit SHA with a `# frozen:` comment, at
the same versions.
- .gitleaks.toml: the default rules plus a permit-api-key rule for
permit_key_[A-Za-z0-9]{20,}, which GitHub secret scanning does not
detect. Allowlists cover the false positives a full-history scan
finds (doc placeholders and samples, the docs search widget key, test
fixtures, the app tests' local Gitea), each limited to the rules that
fire on it.
- pre-commit.yml: pre-commit 4.7.0 (was `<4`) so the hooks run in CI;
zizmor runs in its own step with the job token for its online audits
(impostor commits, known-vulnerable actions, ref and version-comment
mismatches), the other hooks without it. Actions pinned by SHA,
persist-credentials: false, contents: read, a 20-minute timeout,
ubuntu-24.04, bash -euo pipefail, and older PR runs cancelled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A secret-scan job in tests.yml, required through the CI job, runs gitleaks 8.30.1 (checksum-verified binary, .gitleaks.toml rules) over the commits the run brings in: base..head for a PR, before..sha for a push, the head commit for a new branch or a manual run. Older history is out of scope, so it cannot block an unrelated PR. The job fails on findings (printed redacted), when gitleaks does not complete, when the range has no commits, and when gitleaks reports 0 commits scanned: a shallow clone, or a git log format it cannot parse, would otherwise pass having checked nothing. Its output runs inside ::stop-commands:: because it quotes lines from the commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follows permit-golang's tools/binaries.lock, fetch-tool.sh and prek-count.sh: - .github/binaries.lock pins release binaries by version and sha256 for darwin and linux, amd64 and arm64: gitleaks 8.30.1, prek 0.5.9, trivy 0.76.1, trufflehog 3.99.7, uv 0.13.6, each the newest release at least 7 days old, with the sums from the release's own checksum files. .github/scripts/fetch-tool.sh <name> checks the sha256 before unpacking and installs bin/<name>, which .gitignore now ignores. It deletes nothing: the download stays under RUNNER_TEMP or TMPDIR. - pre-commit.yml (job still named pre-commit, the required check) installs prek that way and runs .github/scripts/prek-count.sh, which runs every hook on every file and fails unless EXPECTED_HOOKS (12) hooks passed or had nothing to check, so a skipped or dropped hook cannot pass unnoticed. zizmor gets GH_TOKEN for its online audits. - .pre-commit-config.yaml: minimum_prek_version 0.5.5, prek update with a 7-day cooldown and frozen SHAs, zizmor reads .github/zizmor.yml (every action pinned to a full SHA, GitHub's included), and shellcheck 0.11.0.1 and shfmt 3.14.1 check .github/scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A header that states the rules every entry follows. - No labels: Dependabot's defaults (dependencies plus the ecosystem) apply, and it creates them when missing. - Version groups say applies-to: version-updates with patterns "*". - deps for what the packages and images install (pip, /docker); deps-dev for actions, hooks, the docs site and the test-only seed image. The actions and hooks group is named tooling. - No security group on docker and pre-commit entries: Dependabot has no security updates for either. The entries, the pip strategy, the shared-majors group and the reasoned ignores are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ports permit-golang's PR-time secret scan, dependency review and aggregate check to tests.yml: - secrets (was secret-scan) installs gitleaks with fetch-tool.sh and runs .github/scripts/gitleaks-range.sh: base..head for a PR, before..after for a push (origin/master..after for a new branch), origin/master..HEAD for a manual run. gitleaks gets .gitleaks.toml by path, runs from an empty directory with an empty ignore file, no GITLEAKS_ variables and no user or system git config, reports lines marked gitleaks:allow, reads binary-looking files as text and scans what merge commits change. The commit count comes from git rev-list, and an empty range fails on a PR. Exit 1 for findings (redacted), 2 when the scan did not run. - .gitleaks.toml: fake keys that start with permit_key_FAKE are allowed anywhere. - dependency-review (pull requests only): actions/dependency-review- action v5.0.0 fails on a new dependency with a high or critical vulnerability and comments on failure. - The CI job decides with .github/scripts/ci-aggregate.sh: every job must succeed in the runs that select it (dependency-review on pull requests only), and the count of needed jobs must be EXPECTED_JOBS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ports permit-golang's security-scan.sh, digest.sh, trivy.sh and vuln.sh to OPAL, with a new security.yml (schedule plus a manual mode input) instead of modes in the CI workflow, and notify.sh in place of the Go notify and secreport tools. - vuln.sh resolves the published packages' requires files with uv pip compile (CPython 3.12, manylinux, wheels only) into a floor (lowest direct versions) and a ceiling (newest), and with --prepare leaves the trees for security-scan.sh. Without it, it runs trivy.sh block on both. - trivy.sh scans a pinned requirements file (Trivy must list one package per pin) or a published image (at least one package), from an empty directory with no Trivy config or TRIVY_ variables, and writes findings as JSON lines. - security-scan.sh (Mon and Thu, or mode security): floor, ceiling, the latest opal-client and opal-server from PyPI, the permitio/opal-server and opal-client :latest images, the open Dependabot alerts, gitleaks over the full history with accepted fingerprint digests, and TruffleHog --results=verified on manual runs. Findings are reported and pass; a source that did not complete or a verified secret fails. Private findings are counted in the job summary and listed in Slack only. - digest.sh (Mon, or mode weekly): open Dependabot pull requests, the last security scan, and newer releases of .github/binaries.lock entries that are at least 7 days old. - A missing DEPENDABOT_ALERTS_TOKEN skips the alerts source and a missing SECURITY_SLACK_WEBHOOK_URL skips the post, each with a line in the job summary; without GITLEAKS_HISTORY_ACCEPT_SHA256 every history finding is reported as new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every job in tests.yml, pre-commit.yml and docs-build.yml now states contents: read, next to the workflow-level block, as on_release.yml and security.yml already do. This closes CodeQL's "Workflow does not contain permissions" (actions/missing-workflow-permissions) alerts on tests.yml (build, e2e-tests, e2e-tests-alpine) and pre-commit.yml; the on_release.yml alert is closed by the permissions blocks of the release commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The security scan mints a per-run installation token from the security GitHub App (SECURITY_APP_CLIENT_ID, SECURITY_APP_PRIVATE_KEY in the security-scan environment), scoped to this repository with only "Dependabot alerts: read", instead of taking a stored token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SECURITY_SLACK_WEBHOOK_URL is a secret of the security-scan environment, not of the repository, so the digest job, which ran outside it, read an empty webhook: notify.sh then skips the post with a warning and the job stays green. The digest job now runs in the environment too, which keeps the webhook limited to master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The group was keyed on inputs.mode or github.event.schedule, so a
scheduled scan ("security-23 4 * * 1,4") and a manual scan
("security-security") were in different groups and could run at the
same time, which the comment said they would not. The group is now
security-scan or security-digest, whichever way the run started.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comment said the pypi environment scopes the PyPI token to the publish job. PYPI_TOKEN is a repository secret and the environment holds no secrets, so every workflow can still read it. The comment now says so, and that moving the secret into the environment would scope it. Comment only; the workflow is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests.yml never sets ADVISORY_JOBS, so the option that let a listed job fail without failing CI had no user here. It is removed with its docs; every result other than success, or a skip in a run that selects the job, fails CI as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two scripts decide the required CI and pre-commit checks, and their failure paths were tested only by hand. Each test runs its script on made-up results (job results for ci-aggregate.sh, a fake prek for prek-count.sh) and checks the exit code of every case: success, a failed, cancelled or skipped job, a hook skipped by SKIP, a wrong count, malformed input and wrong arguments. The pre-commit job runs both after the hooks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The secret scan's failure paths were tested only by hand. The test builds a throwaway repository with the scan's files, commits random Permit API keys made at run time, and checks gitleaks-range.sh's exit code for a clean range, a range that adds a key (also on a line with gitleaks:allow), an empty range with and without --if-empty notice, the zero SHA, revisions that are not commits and wrong arguments. It checks that the finding comes from the permit-api-key rule of .gitleaks.toml and that no output shows a key. The secrets job runs it before the scan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
notify.sh exits 2 when SECURITY_SLACK_WEBHOOK_URL is not an https URL, and security-scan.sh and digest.sh pass that code on, but neither header listed it. security-scan.sh's header also named jq alone, though the script checks for jq, gh and curl. Comments only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
.pre-commit-config.yaml now sets minimum_prek_version 0.5.5 and the pre-commit job runs the hooks with prek, but CONTRIBUTING.md still told contributors to run pre-commit install and pre-commit run. It now names prek and the version the config needs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
omer9564
approved these changes
Nov 9, 2026
This branch has not been deployed
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
Fixes the release workflow, which failed for 0.10.0-rc.6, and brings the GitHub Actions setup up to Permit's repo standard: pinned actions, least-privilege tokens, workflow linting, Dependabot, secret scanning on PRs, and a scheduled security scan. The setup follows permitio/permit-golang#78.
Changelog
Fixed
3.11instead of3.11.8, which Ubuntu 26.04 runners don't have. This fixes the failed 0.10.0-rc.6 publish.Added
CIcheck.Changed
permissions, and no checkout persists its credentials.pypienvironment, and image builds no longer use a registry cache. The unused token env and the unused docker-compose v1 download are removed.ubuntu-24.04.CIcheck also runs on stacked PRs.Release fix
ubuntu-latestis now Ubuntu 26.04, andactions/setup-pythonhas no 3.11.8 build for it, so every publish job of 0.10.0-rc.6 failed at "Python setup":Both publish jobs now set up
python-version: '3.11'. The failed run stopped before any build, so nothing was published under 0.10.0-rc.6.on_release.yml: every behavior change
Each action is pinned to the exact commit the 0.10.0-rc.5 and rc.6 runs downloaded, so the action code is unchanged: checkout v2.8.0, setup-python v5.6.0, login v2.2.0, setup-qemu v3.7.0, setup-buildx v3.12.0, build-push v6.19.2, upload-release-asset v1.7.5, pypi-publish v1.14.3.
permissions: {}at the top, and each job gets only what it uses:validate_release: none.build_and_testandpublish_docker_images:contents: read.publish_python_packages:contents: write, for the release assets.id-tokenandpull-requestsgrants are removed. The PyPI steps pass a password, so the publish action never asks for an OIDC token.persist-credentials: falseon every checkout.run:blocks throughenv:.pypienvironment. The environment has no protection rules, so this adds no approval step.TOKEN_GITHUBenv;docker compose, the v2 plugin).pip installversions are pinned to those the rc.5 run installed (semver 3.1.0, packaging 26.3, wheel 0.48.0).Workflows
In tests.yml, pre-commit.yml and docs-build.yml:
permissionsblock;persist-credentials: false;ubuntu-24.04;tests.yml also changes how its checks run:
pull_requesttrigger has no base-branch filter, so the requiredCIcheck runs on stacked PRs too.CIaggregate checks its jobs through.github/scripts/ci-aggregate.sh.This closes the five code-scanning alerts "Workflow does not contain permissions" (alerts 1, 2, 3, 4 and 11).
Hooks
.pre-commit-config.yamladds actionlint, zizmor (with.github/zizmor.yml), gitleaks, and shellcheck and shfmt for.github/scripts. Every hook's rev is frozen to its tag's commit.The
pre-commitjob runs the hooks with prek 0.5.9, which it installs from.github/binaries.lockand checks by sha256. The job fails unless all 12 hooks pass. Locally, prek 0.5.5 or later is needed.Secret scanning and dependency review on PRs
secretsjob (part ofCI): runs.github/scripts/gitleaks-range.sh, a checksum-verified gitleaks 8.30.1 over the PR's commit range, redacted..gitleaks.tomladds apermit-api-keyrule. It allowlists, with a comment each, the placeholder values in docs and tests.dependency-reviewjob: fails on high or critical vulnerabilities that a PR's dependency changes add.Dependabot
.github/dependabot.ymlcomes from #957 by @EliMoshkovich (his three commits, kept as authored), plus follow-up commits./dockerand the git-leak seed image), github-actions and pre-commit.toolinggroup.increase-if-necessary, so the packages' minimum versions are not raised to the newest release.Scheduled security scan
security.ymlruns.github/scripts/security-scan.shtwice a week anddigest.shweekly, and can be started by hand. The scan covers:The report goes to the job summary and to Slack. The scan reads the Dependabot alerts with a per-run installation token from a GitHub App that has only "Dependabot alerts: read". No personal access token is used. Until these secrets exist, each part skips and says so in the summary:
SECURITY_SLACK_WEBHOOK_URL, in thesecurity-scanenvironment. Both jobs run in that environment, so the webhook is available only to runs from master. A manual run must start from master.OPAL_CI_APP_CLIENT_IDandOPAL_CI_APP_PRIVATE_KEY(thepermitio-opal-ciApp), in thesecurity-scanenvironment.GITLEAKS_HISTORY_ACCEPT_SHA256, in thesecurity-scanenvironment. A dependency-vulnerability gate on PRs is not added yet, because the lowest allowed versions currently have known vulnerabilities.Testing
prekover every file: 12 of 12 hooks pass. That includes zizmor with online audits ("No findings to report") and actionlint (exit 0).gitleaks-range.shover this PR's commits: 0 findings..github/scripts/tests/ci-aggregate.test.sh(23 cases) andprek-count.test.sh(12 cases) run in thepre-commitjob.gitleaks-range.test.sh(14 cases, with keys generated at run time) runs in thesecretsjob. Bugs planted in the scripts make these tests fail.CONTRIBUTING.mdnow tells contributors to use prek 0.5.5 or later.gitleaks:allowcomment, on an empty range and on the zero SHA;Supersedes #957 and #898.
🤖 Generated with Claude Code