Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7d1c214
Fix dependency CVEs and gate PRs, releases and a weekly scan
zeevmoney Sep 21, 2026
e5a88c1
Move httpserver_listen_address to conftest so the port is order-indep…
zeevmoney Sep 21, 2026
160f129
TEMP: revert permit/ to origin/main to isolate test_bulk_operations
zeevmoney Sep 21, 2026
f9b4857
Revert "TEMP: revert permit/ to origin/main to isolate test_bulk_oper…
zeevmoney Sep 21, 2026
95a1860
Document the resource instance ident format correctly
zeevmoney Sep 22, 2026
1e6b9e6
Fix the major correctness bugs and enable the xfail tests for 3.0.0
zeevmoney Sep 22, 2026
199c4be
Isolate the end-to-end tests and start the PDP with the env's own key
zeevmoney Sep 22, 2026
3d11c3a
Give the PDP time to warm up and ABAC policy time to propagate
zeevmoney Sep 22, 2026
e7ce61d
Remove dead code and dead dependencies for 3.0.0
zeevmoney Sep 22, 2026
c439afb
Skip only the ABAC decision assertions, with the evidence
zeevmoney Sep 22, 2026
98ea10a
Fix resource_relations.list() and document two backend contracts
zeevmoney Sep 22, 2026
e86f630
Tolerate rate limiting during test teardown
zeevmoney Sep 22, 2026
0865f96
Retry rate-limited requests instead of tolerating them
zeevmoney Sep 22, 2026
0146bb8
Make the rate-limit retry more patient
zeevmoney Sep 22, 2026
e33c160
Point the ABAC skip at PER-16209
zeevmoney Sep 22, 2026
5391be2
Make CheckQuery.context optional for type checkers
zeevmoney Sep 22, 2026
9f795f8
Send the standard "Bearer" scheme in every Authorization header
zeevmoney Sep 23, 2026
306032f
Support Python 3.14 and raise dependency floors that no longer install
zeevmoney Sep 23, 2026
d6aa721
Ship a typed public surface with py.typed (PEP 561)
zeevmoney Sep 23, 2026
1696305
Reduce the ABAC skip to its ticket reference
zeevmoney Sep 23, 2026
76ed0ab
Format the ABAC skip
zeevmoney Sep 23, 2026
ba3ab05
Exclude pydantic 2 releases that bundle a vulnerable pydantic.v1
zeevmoney Sep 24, 2026
92f4e65
Say why setup.py still lists the type files in package_data
zeevmoney Sep 24, 2026
b4e7a69
Remove references to non-public code from comments
zeevmoney Sep 24, 2026
0e3bd01
Build the pydantic candidate grid with loops; drop a no-op mkdir
zeevmoney Sep 24, 2026
87b5f35
Accept audit logs without pdp_config_id or from the GENERIC engine
zeevmoney Sep 24, 2026
aae3163
Check that only GENERIC logs parse as GenericEngineDecisionLog
zeevmoney Sep 24, 2026
69e356a
Test that the blocking client keeps parity with the async one
zeevmoney Sep 24, 2026
027a107
Cover instance attributes and back-references in the parity test
zeevmoney Sep 24, 2026
8aea675
Bind the test HTTP server to a free port
zeevmoney Sep 24, 2026
e826d87
Mark credentialed tests e2e and run the rest offline
zeevmoney Sep 24, 2026
f6a6296
Delete the invite test's resource instance by resource:key
zeevmoney Sep 24, 2026
3d1008c
Send the facade's assign_role and unassign_role through users
zeevmoney Sep 24, 2026
bea1d74
Say the deprecated permit.api methods go away in 4.0
zeevmoney Sep 24, 2026
e69a717
Test resource actions and action groups offline
zeevmoney Sep 24, 2026
008b9c8
Drain the timeout tests' late requests before the next test
zeevmoney Sep 24, 2026
029a894
Check every DeprecationWarning in the facade test
zeevmoney Sep 24, 2026
db2daac
Pass models as well as dicts to the deprecated facade in its test
zeevmoney Sep 24, 2026
47fc662
Fail test_envs.py fast when its API keys are not set
zeevmoney Sep 24, 2026
0686201
Warn on import when permit runs on pydantic 1
zeevmoney Sep 24, 2026
6db7fdf
Document what permit 4.0 removes
zeevmoney Sep 24, 2026
ba653a2
Keep the warnings module out of permit's public names
zeevmoney Sep 24, 2026
d822667
Say how to show or silence the 4.0 deprecation warnings
zeevmoney Sep 24, 2026
a0158b1
Name pip-audit gaps in the audit report and Slack message
zeevmoney Sep 24, 2026
4333fa7
Run pip-audit on every tree without building a venv
zeevmoney Sep 24, 2026
c0352bc
Move the artifact and Slack actions to their Node 24 releases
zeevmoney Sep 24, 2026
b4f20fc
Run pre-commit without the Node 20 cache action
zeevmoney Sep 24, 2026
1f9723e
Drop the actionlint input the action does not declare
zeevmoney Sep 24, 2026
ff25ea9
Keep the SDK's pytest.ini out of the audit script tests
zeevmoney Sep 24, 2026
106fb42
Pin every job to the ubuntu-24.04 runner
zeevmoney Sep 24, 2026
5f87275
Post the audit PR comment the hashFiles guard always skipped
zeevmoney Sep 24, 2026
68f0917
Drop the private pip-audit cache directory
zeevmoney Sep 24, 2026
df74adc
Name only the tree in the Slack pip-audit gap line
zeevmoney Sep 24, 2026
f0314b2
Keep the Trivy install step from logging a scan warning
zeevmoney Sep 24, 2026
43a9da3
Correct two outdated comments in the Security workflow
zeevmoney Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
version: 2
updates:
# Python dependencies (requirements.txt + requirements-dev.txt).
#
# This package publishes open `>=` ranges rather than a lockfile, so a
# Dependabot PR here raises the *floor* consumers are allowed to install on,
# not just the version CI happens to resolve. That is the whole point: the
# floor is the exposure, and the audit gate in security.yml scans it
# explicitly.
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 5
# REQUIRED, not cosmetic. With a setup.py present Dependabot classifies
# this project as a library and defaults to `widen`, which only relaxes
# upper bounds and would never raise a `>=` floor -- so the automation
# would silently never do the one thing this file exists to do.
# `increase` raises the lower bound instead.
versioning-strategy: increase
# Wait 7 days before proposing a release, 14 for a major. A brand-new
# version is the window in which a compromised or yanked package is most
# likely to still be live, and nothing here is urgent enough to need
# day-zero adoption. Security updates are exempt from cooldown by
# Dependabot and still arrive immediately.
cooldown:
default-days: 7
semver-major-days: 14
groups:
minor-and-patch:
update-types: ["minor", "patch"]
ignore:
# pydantic is dual-supported on purpose: permit/utils/pydantic_version.py
# branches on PYDANTIC_VERSION and every model imports from either
# `pydantic` (v1) or `pydantic.v1` (v2 compat shim). A Dependabot major
# bump cannot reason about that and would silently propose dropping v1
# support, so majors are handled by hand. Minor/patch still flow through.
#
# Removal gate: drop this entry once the SDK stops supporting pydantic v1.
- dependency-name: "pydantic"
update-types: ["version-update:semver-major"]
commit-message:
prefix: "deps"
prefix-development: "deps-dev"
labels:
- "dependencies"

# GitHub Actions versions.
# Note: cooldown.semver-major-days is not supported for github-actions --
# Dependabot only honours it on semver-strict ecosystems like pip and npm.
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 5
cooldown:
default-days: 7
groups:
minor-and-patch:
update-types: ["minor", "patch"]
commit-message:
prefix: "deps"
prefix-development: "deps-dev"
labels:
- "dependencies"
148 changes: 148 additions & 0 deletions .github/scripts/audit-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#!/usr/bin/env bash
#
# Scan this package's dependencies for known vulnerabilities.
#
# Usage: audit-deps.sh <output-dir>
#
# Writes four dependency trees to <output-dir>, each as a directory holding a
# file literally named requirements.txt, plus one Trivy report per tree:
#
# runtime-ceiling/ + trivy-runtime-ceiling.json
# requirements.txt alone, current resolution. What a fresh
# `pip install permit` gets today.
# runtime-floor/ + trivy-runtime-floor.json
# runtime-floor-pydantic-v2/ + trivy-runtime-floor-pydantic-v2.json
# requirements.txt alone, lowest-direct. Together, the lowest versions
# the PUBLISHED specs permit -- i.e. real consumer exposure. These are
# the trees that matter most for a library with open `>=` ranges.
# requirements.txt accepts either pydantic major, and lowest-direct
# picks the lowest release it allows, which is a pydantic 1 release, so
# runtime-floor alone never scans a pydantic 2 floor.
# runtime-floor-pydantic-v2 holds pydantic to 2 and scans the lowest
# pydantic 2 (and the pydantic-core it pins) the specs permit.
# dev-ceiling/ + trivy-dev-ceiling.json
# requirements.txt + requirements-dev.txt, current resolution. Test
# tooling only; never ships to a user.
#
# Plus pip-audit-<tree>.json (advisory only) for each of the four trees.
#
# WHY RUNTIME IS COMPILED ALONE. Compiling the runtime and dev files together
# lets a dev tool drag a runtime dependency's floor upward and hide the real
# exposure: when a dev tool needs a newer release of a runtime dependency than
# the floor in requirements.txt, the combined floor resolves that newer release,
# but a consumer installing only `permit` can still land on the older one.
# Scanning the combined floor would silently under-report exactly the versions
# users can actually get.
#
# WHY COMPILE AT ALL. Trivy's pip analyzer only understands `==`. Pointed at
# this repo's raw requirements.txt it reports zero findings and exits 0 -- a
# silently green gate. It also keys on the FILENAME, which is why each tree is
# written to its own directory as `requirements.txt` rather than scanned as a
# loose file (a loose file reports "Not scanned" and, again, exits 0).
set -euo pipefail

OUT="${1:?usage: audit-deps.sh <output-dir>}"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"

# The declared minimum. Resolving at the floor of supported Python is the
# worst case a consumer can legitimately be in.
PYTHON_VERSION="${AUDIT_PYTHON_VERSION:-3.10}"

# A resolved tree with almost nothing in it means the compile silently produced
# garbage. The real runtime tree is ~20 packages; 5 is a floor low enough never
# to false-positive and high enough to catch an empty or truncated compile.
MIN_PACKAGES=5

compile_tree() {
local name="$1" resolution="$2"
shift 2
mkdir -p "${OUT}/${name}"
local args=(--python-version "${PYTHON_VERSION}" --quiet -o "${OUT}/${name}/requirements.txt")
if [ -n "${resolution}" ]; then
args+=(--resolution "${resolution}")
fi
uv pip compile "$@" "${args[@]}"

# Hard post-condition. Without this, an empty tree flows straight into Trivy,
# which writes {"Results": null}, exits 0, and reads as a clean scan.
local count
count=$(grep -c '^[^#[:space:]].*==' "${OUT}/${name}/requirements.txt" || true)
if [ "${count:-0}" -lt "${MIN_PACKAGES}" ]; then
echo "::error title=Dependency resolution failed::Tree '${name}' resolved only ${count:-0} packages (expected at least ${MIN_PACKAGES}). Refusing to scan an empty tree and report it as clean."
exit 1
fi
echo "${name}: ${count} packages"
}

echo "::group::Resolving dependency trees (python ${PYTHON_VERSION})"
# lowest-direct, not lowest: pin the declared bounds to their floor but let
# transitives resolve normally. Plain `lowest` would drag every transitive back
# to its first ever release and drown the report in irrelevant history.
compile_tree runtime-ceiling "" "${REPO_ROOT}/requirements.txt"
compile_tree runtime-floor "lowest-direct" "${REPO_ROOT}/requirements.txt"
echo "pydantic>=2" >"${OUT}/pydantic-v2-constraint.txt"
compile_tree runtime-floor-pydantic-v2 "lowest-direct" "${REPO_ROOT}/requirements.txt" \
--constraints "${OUT}/pydantic-v2-constraint.txt"
compile_tree dev-ceiling "" "${REPO_ROOT}/requirements.txt" "${REPO_ROOT}/requirements-dev.txt"
echo "::endgroup::"

# Trivy exits non-zero on findings when --exit-code is set. We do not set it:
# the report must be produced and rendered whatever the outcome, and the
# pass/fail decision is made once, later, by format_audit.py --gate. One
# decision point means the PR comment and the check can never disagree.
#
# --ignorefile /dev/null is deliberate. Trivy picks up a .trivyignore from the
# working directory automatically and drops matching advisories from the JSON
# entirely -- they vanish from the gate, the PR comment and the Slack message
# with no trace that anything was suppressed. Unfixable advisories already fail
# open (see Finding.blocking), so there is no need for a silent mute button.
for tree in runtime-ceiling runtime-floor runtime-floor-pydantic-v2 dev-ceiling; do
echo "::group::Trivy scan (${tree})"
trivy fs \
--scanners vuln \
--format json \
--ignorefile /dev/null \
--output "${OUT}/trivy-${tree}.json" \
--quiet \
"${OUT}/${tree}"
echo "::endgroup::"
done

# pip-audit is advisory-only. It reports no severity at all, so it can never
# gate; it is here because it reads PYSEC, which sometimes carries a
# Python-specific advisory before it reaches the GHSA feed Trivy uses.
# A pip-audit failure must never fail the job.
#
# Each tree is already a fully pinned `uv pip compile` output, so pip-audit
# reads the pins as written (--no-deps --disable-pip) instead of resolving them
# again in a throwaway venv. That venv is where it used to fail: ensurepip
# exits non-zero on the uv-managed Python, so pip-audit never produced a report.
#
# The exit code cannot tell a failure from a finding: pip-audit exits 1 for
# both. A finished run always writes its report and a failed one writes
# nothing, so each report is deleted before its run and a missing one is the
# failure signal. format_audit.py names every tree without a report in the PR
# comment, the job summary and the Slack message.
#
# PIP_AUDIT_LOGLEVEL=ERROR drops the warning pip-audit logs for --no-deps,
# which recommends hashing the requirements. With --disable-pip, pip-audit only
# checks that hashes are present and never verifies them, so hashing would add
# nothing. Errors, and the summary line, still print.
PIP_AUDIT_VERSION="2.10.1"
for tree in runtime-ceiling runtime-floor runtime-floor-pydantic-v2 dev-ceiling; do
report="${OUT}/pip-audit-${tree}.json"
echo "::group::pip-audit (${tree}, advisory)"
rm -f "${report}"
status=0
PIP_AUDIT_LOGLEVEL=ERROR uv tool run --from "pip-audit==${PIP_AUDIT_VERSION}" pip-audit \
--requirement "${OUT}/${tree}/requirements.txt" \
--no-deps \
--disable-pip \
--format json \
--output "${report}" \
--progress-spinner off || status=$?
if [ ! -s "${report}" ]; then
echo "::warning title=pip-audit did not run::pip-audit exited ${status} without a report for ${tree}, so only Trivy checked that tree. The audit report names it too."
fi
echo "::endgroup::"
done
Loading