pywrit 0.1.7__tar.gz → 0.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pywrit-0.1.7/pywrit.egg-info → pywrit-0.1.8}/PKG-INFO +1 -1
- {pywrit-0.1.7 → pywrit-0.1.8}/pyproject.toml +1 -1
- {pywrit-0.1.7 → pywrit-0.1.8/pywrit.egg-info}/PKG-INFO +1 -1
- {pywrit-0.1.7 → pywrit-0.1.8}/writ_cli/cli.py +163 -3
- {pywrit-0.1.7 → pywrit-0.1.8}/LICENSE +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/README.md +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/pywrit.egg-info/SOURCES.txt +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/pywrit.egg-info/dependency_links.txt +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/pywrit.egg-info/entry_points.txt +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/pywrit.egg-info/requires.txt +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/pywrit.egg-info/top_level.txt +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/setup.cfg +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/tests/test_scan_tsjs.py +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/writ_cli/__init__.py +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/writ_cli/config.py +0 -0
- {pywrit-0.1.7 → pywrit-0.1.8}/writ_cli/risk.py +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
# Distribution name: pywrit (approved by Chad 2026-09-27).
|
|
7
7
|
# The bare name "writ" is taken on PyPI.
|
|
8
8
|
name = "pywrit"
|
|
9
|
-
version = "0.1.
|
|
9
|
+
version = "0.1.8"
|
|
10
10
|
description = "Writ — the gate before the write. Allow/deny gate with a tamper-evident audit log"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.9"
|
|
@@ -11573,6 +11573,7 @@ def main():
|
|
|
11573
11573
|
policy.add_argument("--key", default=config.api_key())
|
|
11574
11574
|
policy.add_argument("--set", nargs=2, metavar=("VERB", "MODE"), action="append", default=[])
|
|
11575
11575
|
policy.add_argument("--env", default="", help="Environment override to get/set: dev, staging, or prod (default: tenant policy)")
|
|
11576
|
+
policy.add_argument("--principal", default="", help="Principal id for a per-principal policy override")
|
|
11576
11577
|
|
|
11577
11578
|
check = sub.add_parser("check", help="ALLOW or DENY a write")
|
|
11578
11579
|
check.add_argument("--key", default=config.api_key())
|
|
@@ -11603,7 +11604,8 @@ def main():
|
|
|
11603
11604
|
revoked = sub.add_parser("revoked", help="List revoked principals (sponsor token)")
|
|
11604
11605
|
revoked.add_argument("--sponsor-token", default=config.sponsor_token())
|
|
11605
11606
|
|
|
11606
|
-
receipts = sub.add_parser("receipts", help="Latest receipts for your tenant")
|
|
11607
|
+
receipts = sub.add_parser("receipts", help="Latest receipts for your tenant, or one receipt by id")
|
|
11608
|
+
receipts.add_argument("receipt_id", nargs="?", default="", help="Fetch a single receipt by id; omit to list")
|
|
11607
11609
|
receipts.add_argument("--key", default=config.api_key())
|
|
11608
11610
|
|
|
11609
11611
|
stream = sub.add_parser("stream", help="Tail the live audit log (SSE stream)")
|
|
@@ -11718,6 +11720,90 @@ def main():
|
|
|
11718
11720
|
gh_disc.add_argument("--totp-code", default="", help="TOTP step-up code")
|
|
11719
11721
|
gh_disc.add_argument("--key", default=config.api_key())
|
|
11720
11722
|
|
|
11723
|
+
principals = sub.add_parser("principals", help="Manage the tenant identity directory")
|
|
11724
|
+
pr_sub = principals.add_subparsers(dest="principals_cmd", required=True)
|
|
11725
|
+
pr_list = pr_sub.add_parser("list", help="List principals")
|
|
11726
|
+
pr_list.add_argument("--key", default=config.api_key())
|
|
11727
|
+
pr_create = pr_sub.add_parser("create", help="Create a principal")
|
|
11728
|
+
pr_create.add_argument("name")
|
|
11729
|
+
pr_create.add_argument("--key", default=config.api_key())
|
|
11730
|
+
pr_rename = pr_sub.add_parser("rename", help="Rename a principal")
|
|
11731
|
+
pr_rename.add_argument("principal_id")
|
|
11732
|
+
pr_rename.add_argument("new_name")
|
|
11733
|
+
pr_rename.add_argument("--key", default=config.api_key())
|
|
11734
|
+
pr_disable = pr_sub.add_parser("disable", help="Disable a principal (pauses its keys)")
|
|
11735
|
+
pr_disable.add_argument("principal_id")
|
|
11736
|
+
pr_disable.add_argument("--key", default=config.api_key())
|
|
11737
|
+
pr_enable = pr_sub.add_parser("enable", help="Re-enable a principal")
|
|
11738
|
+
pr_enable.add_argument("principal_id")
|
|
11739
|
+
pr_enable.add_argument("--key", default=config.api_key())
|
|
11740
|
+
pr_del = pr_sub.add_parser("delete", help="Delete a principal")
|
|
11741
|
+
pr_del.add_argument("principal_id")
|
|
11742
|
+
pr_del.add_argument("--force", action="store_true", help="Revoke remaining keys first")
|
|
11743
|
+
pr_del.add_argument("--key", default=config.api_key())
|
|
11744
|
+
pr_rev = pr_sub.add_parser("revoke-keys", help="Revoke all API keys for a principal")
|
|
11745
|
+
pr_rev.add_argument("principal_id")
|
|
11746
|
+
pr_rev.add_argument("--key", default=config.api_key())
|
|
11747
|
+
|
|
11748
|
+
verbs = sub.add_parser("verbs", help="Manage the tenant verb vocabulary")
|
|
11749
|
+
vb_sub = verbs.add_subparsers(dest="verbs_cmd", required=True)
|
|
11750
|
+
vb_list = vb_sub.add_parser("list", help="List custom verbs")
|
|
11751
|
+
vb_list.add_argument("--key", default=config.api_key())
|
|
11752
|
+
vb_add = vb_sub.add_parser("add", help="Add a custom verb")
|
|
11753
|
+
vb_add.add_argument("name")
|
|
11754
|
+
vb_add.add_argument("--description", default="")
|
|
11755
|
+
vb_add.add_argument("--resolved-verb", default="", help="Canonical verb this aliases")
|
|
11756
|
+
vb_add.add_argument("--key", default=config.api_key())
|
|
11757
|
+
vb_rename = vb_sub.add_parser("rename", help="Rename or redescribe a custom verb")
|
|
11758
|
+
vb_rename.add_argument("name")
|
|
11759
|
+
vb_rename.add_argument("--new-name", default="")
|
|
11760
|
+
vb_rename.add_argument("--description", default="")
|
|
11761
|
+
vb_rename.add_argument("--key", default=config.api_key())
|
|
11762
|
+
vb_retire = vb_sub.add_parser("retire", help="Retire a custom verb")
|
|
11763
|
+
vb_retire.add_argument("name")
|
|
11764
|
+
vb_retire.add_argument("--key", default=config.api_key())
|
|
11765
|
+
|
|
11766
|
+
keys = sub.add_parser("keys", help="Manage tenant API keys")
|
|
11767
|
+
keys_sub = keys.add_subparsers(dest="keys_cmd", required=True)
|
|
11768
|
+
keys_list = keys_sub.add_parser("list", help="List key metadata")
|
|
11769
|
+
keys_list.add_argument("--key", default=config.api_key())
|
|
11770
|
+
keys_create = keys_sub.add_parser("create", help="Create a new API key (TOTP-gated)")
|
|
11771
|
+
keys_create.add_argument("--label", default="", help="Optional label")
|
|
11772
|
+
keys_create.add_argument("--principal-id", default="", help="Assign to a principal")
|
|
11773
|
+
keys_create.add_argument("--totp-code", default="", help="TOTP step-up code")
|
|
11774
|
+
keys_create.add_argument("--key", default=config.api_key())
|
|
11775
|
+
keys_revoke = keys_sub.add_parser("revoke", help="Revoke an API key by id")
|
|
11776
|
+
keys_revoke.add_argument("key_id")
|
|
11777
|
+
keys_revoke.add_argument("--key", default=config.api_key())
|
|
11778
|
+
|
|
11779
|
+
badge = sub.add_parser("badge", help="Dynamic README badge tokens")
|
|
11780
|
+
badge_sub = badge.add_subparsers(dest="badge_cmd", required=True)
|
|
11781
|
+
badge_create = badge_sub.add_parser("create", help="Create a public badge token")
|
|
11782
|
+
badge_create.add_argument("--label", default="gated")
|
|
11783
|
+
badge_create.add_argument("--key", default=config.api_key())
|
|
11784
|
+
badge_revoke = badge_sub.add_parser("revoke", help="Revoke a badge token")
|
|
11785
|
+
badge_revoke.add_argument("token")
|
|
11786
|
+
badge_revoke.add_argument("--key", default=config.api_key())
|
|
11787
|
+
|
|
11788
|
+
vault = sub.add_parser("vault", help="Credential vault")
|
|
11789
|
+
vault_sub = vault.add_subparsers(dest="vault_cmd", required=True)
|
|
11790
|
+
vault_list = vault_sub.add_parser("list", help="List enrolled credentials")
|
|
11791
|
+
vault_list.add_argument("--key", default=config.api_key())
|
|
11792
|
+
|
|
11793
|
+
stats = sub.add_parser("stats", help="24-hour receipt decision counters")
|
|
11794
|
+
stats.add_argument("--key", default=config.api_key())
|
|
11795
|
+
|
|
11796
|
+
lease = sub.add_parser("lease", help="Create a single-use gated-execution lease")
|
|
11797
|
+
lease.add_argument("--verb", required=True)
|
|
11798
|
+
lease.add_argument("--target", required=True)
|
|
11799
|
+
lease.add_argument("--purpose", required=True)
|
|
11800
|
+
lease.add_argument("--ttl", type=int, default=60, help="Lease TTL in seconds")
|
|
11801
|
+
lease.add_argument("--key", default=config.api_key())
|
|
11802
|
+
|
|
11803
|
+
execute = sub.add_parser("execute", help="Execute a gated upstream request using a lease")
|
|
11804
|
+
execute.add_argument("lease_id")
|
|
11805
|
+
execute.add_argument("--key", default=config.api_key())
|
|
11806
|
+
|
|
11721
11807
|
args = parser.parse_args()
|
|
11722
11808
|
if args.cmd == "key":
|
|
11723
11809
|
payload = {"email": args.email}
|
|
@@ -11764,7 +11850,8 @@ def main():
|
|
|
11764
11850
|
|
|
11765
11851
|
_KEY_COMMANDS = {"me", "policy", "check", "receipts", "stream",
|
|
11766
11852
|
"verify-chain", "invite", "token-verify", "manifest-key",
|
|
11767
|
-
"manifests", "github"
|
|
11853
|
+
"manifests", "github", "principals", "verbs", "keys",
|
|
11854
|
+
"badge", "vault", "stats", "lease", "execute"}
|
|
11768
11855
|
if args.cmd in _KEY_COMMANDS and not _require_key(args.key):
|
|
11769
11856
|
return 2
|
|
11770
11857
|
|
|
@@ -11789,7 +11876,12 @@ def main():
|
|
|
11789
11876
|
if args.cmd == "me":
|
|
11790
11877
|
return _request_and_print("GET", "/v1/me", key=args.key)
|
|
11791
11878
|
if args.cmd == "policy":
|
|
11792
|
-
|
|
11879
|
+
if args.principal:
|
|
11880
|
+
url = "/v1/policies/principals/" + urllib.parse.quote(args.principal, safe="")
|
|
11881
|
+
if args.env:
|
|
11882
|
+
url += "?environment=" + urllib.parse.quote(args.env, safe="")
|
|
11883
|
+
else:
|
|
11884
|
+
url = "/v1/policy" if not args.env else f"/v1/policies/environments/{args.env}"
|
|
11793
11885
|
if args.set:
|
|
11794
11886
|
for verb, mode in args.set:
|
|
11795
11887
|
if mode not in MODES:
|
|
@@ -11798,6 +11890,8 @@ def main():
|
|
|
11798
11890
|
return _request_and_print("PUT", url, {verb: mode for verb, mode in args.set}, key=args.key)
|
|
11799
11891
|
return _request_and_print("GET", url, key=args.key)
|
|
11800
11892
|
if args.cmd == "receipts":
|
|
11893
|
+
if args.receipt_id:
|
|
11894
|
+
return _request_and_print("GET", "/v1/receipts/" + urllib.parse.quote(args.receipt_id, safe=""), key=args.key)
|
|
11801
11895
|
return _request_and_print("GET", "/v1/receipts", key=args.key)
|
|
11802
11896
|
if args.cmd == "stream":
|
|
11803
11897
|
return stream_cmd(args)
|
|
@@ -11849,6 +11943,72 @@ def main():
|
|
|
11849
11943
|
if args.github_cmd == "disconnect":
|
|
11850
11944
|
return _request_and_print("POST", "/v1/github/disconnect",
|
|
11851
11945
|
{"installationId": args.installation_id, "totpCode": args.totp_code}, key=args.key)
|
|
11946
|
+
if args.cmd == "principals":
|
|
11947
|
+
if args.principals_cmd == "list":
|
|
11948
|
+
return _request_and_print("GET", "/v1/principals", key=args.key)
|
|
11949
|
+
if args.principals_cmd == "create":
|
|
11950
|
+
return _request_and_print("POST", "/v1/principals", {"name": args.name}, key=args.key)
|
|
11951
|
+
if args.principals_cmd == "rename":
|
|
11952
|
+
return _request_and_print("PATCH", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe=""),
|
|
11953
|
+
{"name": args.new_name}, key=args.key)
|
|
11954
|
+
if args.principals_cmd == "disable":
|
|
11955
|
+
return _request_and_print("PATCH", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe=""),
|
|
11956
|
+
{"disabled": True}, key=args.key)
|
|
11957
|
+
if args.principals_cmd == "enable":
|
|
11958
|
+
return _request_and_print("PATCH", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe=""),
|
|
11959
|
+
{"disabled": False}, key=args.key)
|
|
11960
|
+
if args.principals_cmd == "delete":
|
|
11961
|
+
qs = "?force=1" if args.force else ""
|
|
11962
|
+
return _request_and_print("DELETE", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe="") + qs, key=args.key)
|
|
11963
|
+
if args.principals_cmd == "revoke-keys":
|
|
11964
|
+
return _request_and_print("POST", "/v1/principals/" + urllib.parse.quote(args.principal_id, safe="") + "/revoke",
|
|
11965
|
+
{}, key=args.key)
|
|
11966
|
+
if args.cmd == "verbs":
|
|
11967
|
+
if args.verbs_cmd == "list":
|
|
11968
|
+
return _request_and_print("GET", "/v1/verbs", key=args.key)
|
|
11969
|
+
if args.verbs_cmd == "add":
|
|
11970
|
+
body = {"name": args.name}
|
|
11971
|
+
if args.description:
|
|
11972
|
+
body["description"] = args.description
|
|
11973
|
+
if args.resolved_verb:
|
|
11974
|
+
body["resolvedVerb"] = args.resolved_verb
|
|
11975
|
+
return _request_and_print("POST", "/v1/verbs", body, key=args.key)
|
|
11976
|
+
if args.verbs_cmd == "rename":
|
|
11977
|
+
body = {}
|
|
11978
|
+
if args.new_name:
|
|
11979
|
+
body["name"] = args.new_name
|
|
11980
|
+
if args.description:
|
|
11981
|
+
body["description"] = args.description
|
|
11982
|
+
return _request_and_print("PATCH", "/v1/verbs/" + urllib.parse.quote(args.name, safe=""), body, key=args.key)
|
|
11983
|
+
if args.verbs_cmd == "retire":
|
|
11984
|
+
return _request_and_print("DELETE", "/v1/verbs/" + urllib.parse.quote(args.name, safe=""), key=args.key)
|
|
11985
|
+
if args.cmd == "keys":
|
|
11986
|
+
if args.keys_cmd == "list":
|
|
11987
|
+
return _request_and_print("GET", "/v1/keys", key=args.key)
|
|
11988
|
+
if args.keys_cmd == "create":
|
|
11989
|
+
body = {"totpCode": args.totp_code}
|
|
11990
|
+
if args.label:
|
|
11991
|
+
body["label"] = args.label
|
|
11992
|
+
if args.principal_id:
|
|
11993
|
+
body["principalId"] = args.principal_id
|
|
11994
|
+
return _request_and_print("POST", "/v1/keys", body, key=args.key)
|
|
11995
|
+
if args.keys_cmd == "revoke":
|
|
11996
|
+
return _request_and_print("DELETE", "/v1/keys/" + urllib.parse.quote(args.key_id, safe=""), key=args.key)
|
|
11997
|
+
if args.cmd == "badge":
|
|
11998
|
+
if args.badge_cmd == "create":
|
|
11999
|
+
return _request_and_print("POST", "/v1/badge/tokens", {"label": args.label}, key=args.key)
|
|
12000
|
+
if args.badge_cmd == "revoke":
|
|
12001
|
+
return _request_and_print("POST", "/v1/badge/tokens/revoke", {"token": args.token}, key=args.key)
|
|
12002
|
+
if args.cmd == "vault":
|
|
12003
|
+
if args.vault_cmd == "list":
|
|
12004
|
+
return _request_and_print("GET", "/v1/vault", key=args.key)
|
|
12005
|
+
if args.cmd == "stats":
|
|
12006
|
+
return _request_and_print("GET", "/v1/receipts/stats", key=args.key)
|
|
12007
|
+
if args.cmd == "lease":
|
|
12008
|
+
return _request_and_print("POST", "/v1/leases",
|
|
12009
|
+
{"verb": args.verb, "target": args.target, "purpose": args.purpose, "ttlSeconds": args.ttl}, key=args.key)
|
|
12010
|
+
if args.cmd == "execute":
|
|
12011
|
+
return _request_and_print("POST", "/v1/execute", {"leaseId": args.lease_id}, key=args.key)
|
|
11852
12012
|
if args.cmd in ("revoke", "reinstate", "revoked"):
|
|
11853
12013
|
if not _require_sponsor_token(args.sponsor_token):
|
|
11854
12014
|
return 2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|