Skip to main content

permission-audit

Your Claude Code allowlist is wider than you think — audit it before it hurts you.

tests: 68 passing python 3.10 | 3.11 | 3.12 version 1.0.0 dependencies: none license: MIT

permission-audit demo: a dangerous settings file fails with six errors, a clean file passes

A permission allowlist only grows. You approve a command once and the grant lives forever: Bash(*) approved for one workflow and never revisited, allow rules dead under a wider deny nobody remembers writing, and — worst of all — bypassPermissions committed to a shared repo's .claude/settings.json, where every teammate inherits it. Nobody reads the chain until something runs that shouldn't have.

The human backstop is weaker than it feels. Anthropic reports that Claude Code users approve 97% of permission prompts, and in its controlled study with 1,053 paid testers, human reviewers caught only 13.6% of dangerous commands planted in their sessions (Anthropic, Auto mode is now the default in Claude Code, Aug 2026 — checked 2026-11-04). If the prompts are a rubber stamp, the written rules are the real security boundary — and they accrete unaudited across the settings chain: user settings, the shared project file committed to the repo, and the local file approvals are saved into. permission-audit reads the whole chain for you: every rule inventoried, the documented evaluation order applied, each finding labelled with the rule, the file, and what is actually true about it.

Read-only, and static. It edits nothing, writes nothing, and never runs a command. Permission rules are analysed as text — nothing in the files is evaluated or executed. The test suite proves the read-only claim: every fixture audited through the CLI comes out byte-identical.

The semantics are documented, not guessed. The shipped checks rest on the official Claude Code permissions and settings documentation (code.claude.com/docs/en/permissions and /en/settings, checked 2026-11-04): rules are evaluated deny first, then ask, then allow — specificity does not change the order, a matching ask prompts even when a more specific allow exists, and an allow cannot carve an exception out of a deny. Permission lists merge across files rather than overriding. File precedence, highest first: managed > project local (.claude/settings.local.json) > shared project (.claude/settings.json) > user (~/.claude/settings.json). Where a check goes beyond the documented semantics — pattern coverage — the model is stated in SKILL.md and the finding says "Heuristic" in its own text.

One consequence of the documented order is worth stating plainly, because intuition gets it backwards: a deny rule can never be shadowed by an allow. Deny is evaluated first, so the rules that die are allows covered by a wider deny or ask, and asks covered by a wider deny. That is what SHADOWED_RULE reports.

Quick start

pipx install git+https://github.com/jasveerai88/permission-audit              # or: pip install git+https://github.com/jasveerai88/permission-audit
permission-audit audit ~/.claude/settings.json

That's the whole workflow: one command, one report, an exit code you can gate on — 1 = at least one ERROR finding, 0 = no errors, 2 = input error. Pass one settings file, several (a chain — argument order does not matter, precedence sorts them), or a folder (a .claude/ folder's settings files, or loose settings.json / settings.local.json / managed-settings.json files inside it). Add --json for a machine-readable report. From a source checkout, python3 src/permission_audit.py audit <file> does the same thing. Python 3.10+, standard library only, no dependencies.

What it catches

Every example below is a real line from the shipped fixture examples/dangerous/.claude/settings.json — the full report is below.

Catch What has to be true Real example (from examples/)
Wildcard grants An allow rule that auto-approves a whole tool: Bash(*), a bare tool name, WebFetch(domain:*), a bare * allow Bash(*), bare allow Write, allow WebFetch(domain:*) → BROAD_GRANT (ERROR)
Dangerous modes, shared defaultMode: bypassPermissions or the dangerous-mode prompt switched off, in a file teammates inherit Both set in the shared project file → DANGEROUS_MODE (ERROR), with the ≥ v2.1.257 version nuance stated
Dead rules A rule that can never take effect because a higher tier covers it allow Bash(aws s3 ls) under deny Bash(aws *) → SHADOWED_RULE; the same for a dead ask under a wider deny
Rules that match nothing The tool name is a case-variant of a documented name — matching is exact allow bash(npm test) → RULE_NEVER_MATCHES (ERROR): it does nothing, ever
Filesystem-rooted access A Read / Edit / Write allow rooted at the filesystem itself, no project boundary allow Read(**), allow Edit(//**) → UNBOUNDED_FILE_ACCESS
Accretion The identical rule twice in the merged chain, or a rule fully covered by a broader one in the same list Bash(npm run *) twice → DUPLICATE_RULE; Bash(git log *) under Bash(git *) → REDUNDANT_RULE
Files that load nothing JSON that does not parse, or entries the loader skips examples/invalid-settings.json → INVALID_JSON at line 5, column 5 — including its deny rules, every rule in it is inert

Verdicts: FAIL (at least one ERROR), NO ERROR-CLASS GRANTS (warnings only — rules that can never take effect, or grants broader than they look), CLEAN (no findings). The exit code follows the ERROR count: a warnings-only audit exits 0, because warnings describe dead weight, not live exposure — and a gate that fails on warnings trains people to bypass the gate.

Quick start · What it catches · How it compares · Reference

How it compares

permission-audit Reading the JSON yourself A JSON linter / schema check
Scope The whole chain, merged, in documented precedence order One file at a time, precedence held in your head One file, syntax only
Evaluation order Documented deny → ask → allow applied to every rule You simulate it — and intuition gets shadowing backwards Not modelled
Dead rules Shadowed, duplicate, and never-matching rules flagged, covering rule cited Invisible until the day they matter Not checked — dead rules are valid JSON
Dangerous modes Flagged with scope-aware severity and the version nuance One line among hundreds, easy to scroll past bypassPermissions is a valid string; passes
Output Text report + --json, exit codes 1 / 0 / 2 for CI — Pass/fail on syntax

Honest limits (the short version)

Coverage is a model, not the runtime: real matching involves shell parsing and tool-specific behaviour the prefix model cannot see, so a rule reported dead is provably dead only under the stated model — and heuristics say so in their own text. The tool-name list is a dated snapshot. Settings keys beyond permissions are out of scope. The full limits are below.


Reference

Contents: What it checks · Full reports: dangerous · chain · clean and broken · For agents · Tests · Honest limits

What it checks

Rule Severity What it catches
INVALID_JSON ERROR The file does not parse (exact line / column reported), or the JSON root is not an object. An unparseable file loads nothing — including its deny rules.
PERMISSIONS_SHAPE ERROR / WARN permissions is not an object, or a list is not a list (ERROR — nothing in it can take effect); a single entry the loader skips — non-string, empty, unparseable, empty specifier, or an mcp__ rule with parentheses (WARN).
BROAD_GRANT ERROR / WARN An allow rule that auto-approves a whole tool: Bash(*), a bare tool name, WebFetch(domain:*), or a bare *. ERROR for tools that run code or write (Bash, PowerShell, Edit, Write, MultiEdit, NotebookEdit, WebFetch); WARN for read-only tools. Broad deny/ask rules are never flagged — they are not grants.
DANGEROUS_MODE ERROR / WARN defaultMode: bypassPermissions (either spelling of the key), or skipDangerousModePermissionPrompt: true. ERROR in shared or managed files — committed for everyone who clones; WARN in user / local / unknown files, where it is one person's own machine. The finding carries the version nuance: current Claude Code (≥ v2.1.257) ignores bypassPermissions from project and local files, but older versions honored it from any file.
SHADOWED_RULE WARN A rule that can never take effect because a higher tier covers it: an allow covered by a deny or an ask, an ask covered by a deny. Heuristic — coverage uses the SKILL.md prefix model.
DUPLICATE_RULE WARN The identical rule string twice in the same list across the merged chain. Lists merge, so the copy changes nothing — pure accretion.
UNBOUNDED_FILE_ACCESS WARN A Read / Edit / Write allow rooted at the filesystem itself: **, /*, //**, or a **/-leading pattern with no project boundary. Heuristic — the exact forms judged are listed in SKILL.md.
RULE_NEVER_MATCHES ERROR The tool name is a case-variant of a documented tool name (bash(npm test)) — rules match canonical names exactly, so the rule matches nothing, ever. Unknown tool names are left alone: the tool list grows.
REDUNDANT_RULE INFO A rule fully covered by a broader rule in the same list (Bash(git log *) under Bash(git *)). Coverage by a wildcard-everything rule is excluded — that rule is already the BROAD_GRANT finding.
BYPASS_DISABLED INFO permissions.disableBypassPermissionsMode: "disable" is set — bypass mode is fenced off machine-wide. The good kind of finding.

Every finding names the file, and rule findings name the list and the rule text. Exit codes: 1 when any ERROR finding exists, 0 otherwise, 2 for input errors (missing file, unreadable input, folder with no settings files) — always as a clean {"error": ...} object in --json mode, never a traceback.

A real run — a bloated shared settings file

examples/dangerous/.claude/settings.json is a constructed project file with every fault planted: a wildcard-everything grant, a bare Write, WebFetch(domain:*), bypassPermissions and the dangerous-mode prompt switched off in a shared file, a dead allow, a dead ask, an exact duplicate, three entries the loader skips, a case-variant tool name, and two filesystem-rooted file grants. Verbatim output:

$ permission-audit audit examples/dangerous/.claude/settings.json
permission-audit v1.0.0 — audit · 1 settings file · scope project
17 permission rules in the chain · effective defaultMode: bypassPermissions (from examples/dangerous/.claude/settings.json)
Read-only: it edits nothing and never runs a command. Permission rules are analysed as static text — nothing in the files is evaluated or executed.
Documented semantics: rules are evaluated deny first, then ask, then allow — specificity does not change the order; permission lists merge across scopes; file precedence is managed > project local > shared project > user.

INVENTORY (17 rules):
  examples/dangerous/.claude/settings.json (project scope) — 17 rules · defaultMode: bypassPermissions
    allow Bash(*)
    allow Bash(npm run *)
    allow Bash(npm run *)
    allow Bash(aws s3 ls)
    allow Bash(git *)
    allow Bash(git log *)
    allow bash(npm test)
    allow Read(**)
    allow Edit(//**)
    allow WebFetch(domain:*)
    allow Write
    ask   Bash(deploy *)
    ask   Bash(git push *)
    deny  Bash(aws *)
    deny  Bash(deploy *)
    deny  Bash(rm -rf *)
    deny  Read(./.env)

ERRORS — grants broader than their text, or rules that can never work as configured (6):
[BROAD_GRANT] examples/dangerous/.claude/settings.json · allow · Bash(*)
  allow rule "Bash(*)" auto-approves every Bash call — a wildcard-everything grant. Grants like this are the ones an accreted allowlist hides: it was presumably added for one workflow and now covers all of them
  fix: narrow it to the pattern the workflow actually needs (e.g. Bash(npm run *)), or remove it and let the calls prompt
[BROAD_GRANT] examples/dangerous/.claude/settings.json · allow · WebFetch(domain:*)
  allow rule "WebFetch(domain:*)" auto-approves every WebFetch call, to any domain — a wildcard-everything grant. Grants like this are the ones an accreted allowlist hides: it was presumably added for one workflow and now covers all of them
  fix: narrow it to the pattern the workflow actually needs (e.g. Bash(npm run *)), or remove it and let the calls prompt
[BROAD_GRANT] examples/dangerous/.claude/settings.json · allow · Write
  allow rule "Write" auto-approves every Write call — a wildcard-everything grant. Grants like this are the ones an accreted allowlist hides: it was presumably added for one workflow and now covers all of them
  fix: narrow it to the pattern the workflow actually needs (e.g. Bash(npm run *)), or remove it and let the calls prompt
[DANGEROUS_MODE] examples/dangerous/.claude/settings.json
  permissions.defaultMode is "bypassPermissions" in a SHARED settings file — it is committed to the repo and ships to every teammate who clones it: sessions start with permission prompts skipped. Version nuance: current Claude Code (>= v2.1.257) ignores bypassPermissions set in project and local files — but older versions honored it from any file, and a committed bypass is the loaded gun of a settings file either way
  fix: remove bypassPermissions from the shared file; anyone who wants it can set it in their own user settings, per machine, on purpose
[DANGEROUS_MODE] examples/dangerous/.claude/settings.json
  skipDangerousModePermissionPrompt is true in a SHARED settings file, committed for every teammate to inherit: the one warning that stands between a user and bypassPermissions mode is switched off
  fix: remove the key from the shared file; leave the dangerous-mode prompt on
[RULE_NEVER_MATCHES] examples/dangerous/.claude/settings.json · allow · bash(npm test)
  "bash(npm test)" names the tool "bash" — permission rules match canonical tool names exactly, so this rule can never match the Bash tool and does nothing (for deny and ask rules Claude Code shows a startup warning; the rule still does not match)
  fix: change the tool name to "Bash"

WARNINGS — rules that can never take effect, or probably do not do what you intend (8):
[DUPLICATE_RULE] examples/dangerous/.claude/settings.json · allow · Bash(npm run *)
  the allow rule "Bash(npm run *)" appears more than once in the chain (first in examples/dangerous/.claude/settings.json) — permission lists merge across scopes, so the extra copy changes nothing; it is the allowlist accreting
  fix: keep the rule in one file (the scope it belongs to) and remove the copies
[PERMISSIONS_SHAPE] examples/dangerous/.claude/settings.json · allow
  "" cannot be parsed as a permission rule (Tool or Tool(specifier)) — it matches nothing and the loader skips it
  fix: write it as a tool name, or as Tool(specifier) with balanced parentheses
[PERMISSIONS_SHAPE] examples/dangerous/.claude/settings.json · allow · Bash(npm run
  "Bash(npm run" cannot be parsed as a permission rule (Tool or Tool(specifier)) — it matches nothing and the loader skips it
  fix: write it as a tool name, or as Tool(specifier) with balanced parentheses
[PERMISSIONS_SHAPE] examples/dangerous/.claude/settings.json · deny · 42
  an entry in the deny list is an int, not a string — Claude Code skips malformed permission rules (a documented Settings Warning), so this entry does nothing
  fix: make every entry a rule string: a tool name, or Tool(specifier)
[SHADOWED_RULE] examples/dangerous/.claude/settings.json · allow · Bash(aws s3 ls)
  the allow rule "Bash(aws s3 ls)" can never take effect: the deny rule "Bash(aws *)" in examples/dangerous/.claude/settings.json matches everything it matches, and rules are evaluated deny first, then ask, then allow (documented) — the call is denied instead. Heuristic: coverage uses the prefix model in SKILL.md
  fix: remove the dead rule, or narrow the covering rule if the exception was the intent (an allow cannot carve an exception out of a deny — documented)
[SHADOWED_RULE] examples/dangerous/.claude/settings.json · ask · Bash(deploy *)
  the ask rule "Bash(deploy *)" can never take effect: the deny rule "Bash(deploy *)" in examples/dangerous/.claude/settings.json matches everything it matches, and rules are evaluated deny first, then ask, then allow (documented) — the call is denied instead. Heuristic: coverage uses the prefix model in SKILL.md
  fix: remove the dead rule, or narrow the covering rule if the exception was the intent (an allow cannot carve an exception out of a deny — documented)
[UNBOUNDED_FILE_ACCESS] examples/dangerous/.claude/settings.json · allow · Edit(//**)
  allow rule "Edit(//**)" grants Edit access rooted at the filesystem itself — the path pattern has no project boundary, so it reaches every file the user account can. Heuristic: read off the specifier's shape; the exact forms judged are listed in SKILL.md
  fix: root the pattern inside the project (e.g. Read(./src/**)), or drop the rule and let reads outside the project prompt
[UNBOUNDED_FILE_ACCESS] examples/dangerous/.claude/settings.json · allow · Read(**)
  allow rule "Read(**)" grants Read access rooted at the filesystem itself — the path pattern has no project boundary, so it reaches every file the user account can. Heuristic: read off the specifier's shape; the exact forms judged are listed in SKILL.md
  fix: root the pattern inside the project (e.g. Read(./src/**)), or drop the rule and let reads outside the project prompt

INFO — worth knowing (1):
[REDUNDANT_RULE] examples/dangerous/.claude/settings.json · allow · Bash(git log *)
  the allow rule "Bash(git log *)" is fully covered by the allow rule "Bash(git *)" in examples/dangerous/.claude/settings.json — the outcome is the same with or without it. Pure accretion: the list grows, nothing changes
  fix: remove the narrower rule, or keep both knowingly — the broad rule is the one doing the work

Summary: 15 findings · 6 errors · 8 warnings · 1 info
Verdict: FAIL — 6 error(s): at least one grant is wildcard-broad, a dangerous mode is set where others inherit it, or a rule can never match. Fix the errors before trusting this allowlist.
(exit code 1)

Note what is not flagged: ask Bash(git push *) sits under allow Bash(git *), but ask is evaluated before allow, so the prompt still fires — no finding. And deny Bash(rm -rf *) is exactly as live as it looks: nothing outranks deny.

A chain — three files, one merged verdict

The chain example ships a user file, a shared project file, and a project-local file under examples/chain/. The arguments below are deliberately scrambled — precedence, not argument order, sorts the chain:

$ permission-audit audit examples/chain/project/.claude/settings.json examples/chain/home/.claude/settings.json examples/chain/project/.claude/settings.local.json --home examples/chain/home
permission-audit v1.0.0 — audit · 3 settings files · chain order (highest precedence first): local > project > user
6 permission rules in the chain · effective defaultMode: acceptEdits (from examples/chain/project/.claude/settings.json)
Read-only: it edits nothing and never runs a command. Permission rules are analysed as static text — nothing in the files is evaluated or executed.
Documented semantics: rules are evaluated deny first, then ask, then allow — specificity does not change the order; permission lists merge across scopes; file precedence is managed > project local > shared project > user.

INVENTORY (6 rules):
  examples/chain/project/.claude/settings.local.json (local scope) — 1 rule
    allow Bash(npm run build)
  examples/chain/project/.claude/settings.json (project scope) — 2 rules · defaultMode: acceptEdits
    allow Bash(make *)
    deny  Bash(npm *)
  examples/chain/home/.claude/settings.json (user scope) — 3 rules
    allow Bash(make *)
    allow Bash(npm run build)
    allow Read(./docs/*)

WARNINGS — rules that can never take effect, or probably do not do what you intend (4):
[SHADOWED_RULE] examples/chain/project/.claude/settings.local.json · allow · Bash(npm run build)
  the allow rule "Bash(npm run build)" can never take effect: the deny rule "Bash(npm *)" in examples/chain/project/.claude/settings.json matches everything it matches, and rules are evaluated deny first, then ask, then allow (documented) — the call is denied instead. Heuristic: coverage uses the prefix model in SKILL.md
  fix: remove the dead rule, or narrow the covering rule if the exception was the intent (an allow cannot carve an exception out of a deny — documented)
[DUPLICATE_RULE] examples/chain/home/.claude/settings.json · allow · Bash(make *)
  the allow rule "Bash(make *)" appears more than once in the chain (first in examples/chain/project/.claude/settings.json) — permission lists merge across scopes, so the extra copy changes nothing; it is the allowlist accreting
  fix: keep the rule in one file (the scope it belongs to) and remove the copies
[DUPLICATE_RULE] examples/chain/home/.claude/settings.json · allow · Bash(npm run build)
  the allow rule "Bash(npm run build)" appears more than once in the chain (first in examples/chain/project/.claude/settings.local.json) — permission lists merge across scopes, so the extra copy changes nothing; it is the allowlist accreting
  fix: keep the rule in one file (the scope it belongs to) and remove the copies
[SHADOWED_RULE] examples/chain/home/.claude/settings.json · allow · Bash(npm run build)
  the allow rule "Bash(npm run build)" can never take effect: the deny rule "Bash(npm *)" in examples/chain/project/.claude/settings.json matches everything it matches, and rules are evaluated deny first, then ask, then allow (documented) — the call is denied instead. Heuristic: coverage uses the prefix model in SKILL.md
  fix: remove the dead rule, or narrow the covering rule if the exception was the intent (an allow cannot carve an exception out of a deny — documented)

Summary: 4 findings · 0 errors · 4 warnings · 0 info
Verdict: NO ERROR-CLASS GRANTS — but 4 warning(s) describe rules that can never take effect or grants broader than they look.
(exit code 0)

The project deny Bash(npm *) kills the Bash(npm run build) allow in both other files — cross-file shadowing is the whole point of auditing the chain instead of one file at a time. (--home points the user-scope inference at the fixture home; on a real machine it defaults to your actual home directory.)

A clean file, and a broken one

$ permission-audit audit examples/clean/.claude/settings.json
permission-audit v1.0.0 — audit · 1 settings file · scope project
6 permission rules in the chain · effective defaultMode: default (from examples/clean/.claude/settings.json)
Read-only: it edits nothing and never runs a command. Permission rules are analysed as static text — nothing in the files is evaluated or executed.
Documented semantics: rules are evaluated deny first, then ask, then allow — specificity does not change the order; permission lists merge across scopes; file precedence is managed > project local > shared project > user.

INVENTORY (6 rules):
  examples/clean/.claude/settings.json (project scope) — 6 rules · defaultMode: default
    allow Bash(npm run *)
    allow Bash(git status)
    allow Read(./src/**)
    ask   Bash(git push *)
    deny  Bash(rm -rf *)
    deny  Read(./.env)

No findings — every rule passes the static rules.
Summary: 0 findings · 0 errors · 0 warnings · 0 info
Verdict: CLEAN — no broad grants, dead rules or dangerous modes found by the static rules.
(exit code 0)
$ permission-audit audit examples/invalid-settings.json
permission-audit v1.0.0 — audit · 1 settings file · scope unknown
0 permission rules in the chain · no defaultMode set in the chain
Read-only: it edits nothing and never runs a command. Permission rules are analysed as static text — nothing in the files is evaluated or executed.
Documented semantics: rules are evaluated deny first, then ask, then allow — specificity does not change the order; permission lists merge across scopes; file precedence is managed > project local > shared project > user.

INVENTORY (0 rules):
  examples/invalid-settings.json (unknown scope) — 0 rules
    (file does not parse — no rules load from it)

ERRORS — grants broader than their text, or rules that can never work as configured (1):
[INVALID_JSON] examples/invalid-settings.json
  the file is not valid JSON — line 5, column 5: Expecting value. Claude Code loads nothing from a file it cannot parse: every permission rule in it is inert, including its deny rules
  fix: fix the JSON at the reported position (a trailing comma and unquoted keys are the usual causes)

Summary: 1 findings · 1 errors · 0 warnings · 0 info
Verdict: FAIL — 1 error(s): at least one grant is wildcard-broad, a dangerous mode is set where others inherit it, or a rule can never match. Fix the errors before trusting this allowlist.
(exit code 1)

For agents

--json emits one object: per-file records (scope, rule count, default modes, parse status), the full rule inventory with parsed tool and specifier, the effective default mode and which file set it, findings (rule, severity, file, list, rule_text, message, fix), and summary counts that recount exactly against the findings. Input errors — including argument errors — are a single JSON object with an error key and exit 2. SKILL.md states the full rule model, the coverage arithmetic, the documented-semantics citations, and what the tool deliberately does not claim — and makes permission-audit itself usable as a skill: point your agent at this repo's SKILL.md and it will audit settings chains with the documented semantics in hand.

Tests

python3 -m unittest discover -s tests

68 tests: every rule in both directions, the coverage model pinned pair by pair (including the documented trailing-space nuance: Bash(ls *) also matches bare ls), chain precedence and effective-mode resolution, per-fixture hand-counted tables, JSON recount, exit codes 0/1/2 with no-traceback assertions, and the read-only proof — fixture bytes hashed before and after CLI runs.

Honest limits

  • Coverage is a model, not the runtime. Real matching involves argument semantics, shell parsing, and tool-specific behaviour the prefix model cannot see. A rule reported dead is provably dead only under the stated model; a rule not reported may still never fire in practice. Findings say "Heuristic" where this applies.
  • Tool inventory is a snapshot. The never-matches check compares case-variants against the documented tool names as of 2026-11-04; new tools simply are not case-checked until the list is refreshed.
  • Scope is inferred from file name and location. A file passed by an unusual path is unknown scope, and mode findings fall back to the cautious severity.
  • Settings keys beyond permissions are out of scope (hooks have their own auditor — see hook-doctor). additionalDirectories widens file access in practice and is deliberately not modelled in v1.0.0.

Contributing

See CONTRIBUTING.md. The rule table is the product: every check rests on documented semantics (cited, with the date checked) or is labelled a heuristic in its own finding text. Found a settings file permission-audit misjudges — a dead rule it misses or a finding it shouldn't fire? Open an issue with a constructed fixture; never a real allowlist, which is a map of what a machine will run without asking.

Security

permission-audit is itself a security tool, so its own posture is simple: it has no network code, no subprocess code, no dependencies, and it never evaluates or executes a command it reads. See SECURITY.md for how to report a problem.

License

MIT — see LICENSE.

  • skill-vet — the other half of pre-flight for agent setups: vets a whole agent skill pack before you install it, blocking only on proven stealing / spying / hidden-sending combinations.

Support

If permission-audit saves you a 2 a.m. incident, consider sponsoring.

Metadata

Release files for permission-audit 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for permission-audit 1.0.0
File Size Uploaded Provenance Actions
permission_audit-1.0.0.tar.gz 37.8 kB With provenance Details for permission_audit-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for permission-audit 1.0.0
File Interpreter ABI Platform Actions
permission_audit-1.0.0-py3-none-any.whl Python 3 none any Details for permission_audit-1.0.0-py3-none-any.whl

Total release size: 62.4 kB

Release files / permission_audit-1.0.0.tar.gz

Download URL permission_audit-1.0.0.tar.gz
Size 37.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b52de9c2e13e6bfa0f3b640e2f196ea27fac2eee5c1fca6dce32d89ff6307786
BLAKE2b-256 checksum
How to use checksums
01ef7fa3c756bf52ec5a8fd988d92c6e73d2e539c4cdb44e061fa368bddd7f6f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Release files / permission_audit-1.0.0-py3-none-any.whl

Download URL permission_audit-1.0.0-py3-none-any.whl
Size 24.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ebd34da520277338aecd535c36cbcf819710818748fa5c5c9b8313fc0d0e24b5
BLAKE2b-256 checksum
How to use checksums
22532d3a050acc6074f75d405c82d40de53f1181879076b5249262f588013ecb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Security for permission-audit 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for permission_audit-1.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page