pywrit 0.1.6__tar.gz → 0.1.7__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.6/pywrit.egg-info → pywrit-0.1.7}/PKG-INFO +1 -1
- {pywrit-0.1.6 → pywrit-0.1.7}/pyproject.toml +1 -1
- {pywrit-0.1.6 → pywrit-0.1.7/pywrit.egg-info}/PKG-INFO +1 -1
- {pywrit-0.1.6 → pywrit-0.1.7}/writ_cli/cli.py +91 -1
- {pywrit-0.1.6 → pywrit-0.1.7}/LICENSE +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/README.md +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/pywrit.egg-info/SOURCES.txt +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/pywrit.egg-info/dependency_links.txt +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/pywrit.egg-info/entry_points.txt +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/pywrit.egg-info/requires.txt +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/pywrit.egg-info/top_level.txt +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/setup.cfg +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/tests/test_scan_tsjs.py +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/writ_cli/__init__.py +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/writ_cli/config.py +0 -0
- {pywrit-0.1.6 → pywrit-0.1.7}/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.7"
|
|
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"
|
|
@@ -11035,6 +11035,15 @@ def scan_cmd(args):
|
|
|
11035
11035
|
mpath = _pathlib.Path(args.manifest_out)
|
|
11036
11036
|
mpath.write_text(_json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
|
11037
11037
|
print(" manifest written: %s" % mpath)
|
|
11038
|
+
if getattr(args, "push_manifest", False):
|
|
11039
|
+
if not args.key:
|
|
11040
|
+
print("error: --push-manifest needs --key", file=sys.stderr)
|
|
11041
|
+
return 2
|
|
11042
|
+
code, text = request("POST", "/v1/manifests", manifest, key=args.key)
|
|
11043
|
+
if code != 0:
|
|
11044
|
+
print(text, file=sys.stderr)
|
|
11045
|
+
return code
|
|
11046
|
+
print(" manifest submitted: %s" % text)
|
|
11038
11047
|
|
|
11039
11048
|
if getattr(args, "push_policy", False):
|
|
11040
11049
|
if not args.key:
|
|
@@ -11242,6 +11251,13 @@ def _require_sponsor_token(token):
|
|
|
11242
11251
|
return True
|
|
11243
11252
|
|
|
11244
11253
|
|
|
11254
|
+
def _load_json_file(path):
|
|
11255
|
+
p = _pathlib.Path(path)
|
|
11256
|
+
if not p.exists():
|
|
11257
|
+
raise FileNotFoundError("file not found: %s" % path)
|
|
11258
|
+
return json.loads(p.read_text(encoding="utf-8"))
|
|
11259
|
+
|
|
11260
|
+
|
|
11245
11261
|
# ---------------------------------------------------------------------------
|
|
11246
11262
|
# Agent Action Report
|
|
11247
11263
|
# ---------------------------------------------------------------------------
|
|
@@ -11640,6 +11656,8 @@ def main():
|
|
|
11640
11656
|
help="API key (for --push-policy / --manifest-out)")
|
|
11641
11657
|
scan.add_argument("--manifest-out", default="",
|
|
11642
11658
|
help="Write a signed CI attestation manifest to PATH")
|
|
11659
|
+
scan.add_argument("--push-manifest", action="store_true",
|
|
11660
|
+
help="After writing --manifest-out, POST the signed manifest to the gate (needs --key)")
|
|
11643
11661
|
scan.add_argument("--manifest-key", default=os.environ.get("WRIT_MANIFEST_KEY", ""),
|
|
11644
11662
|
help="HMAC key for signing the manifest (default: WRIT_MANIFEST_KEY env)")
|
|
11645
11663
|
scan.add_argument("--commit-sha", default="",
|
|
@@ -11662,6 +11680,44 @@ def main():
|
|
|
11662
11680
|
manifest_key = sub.add_parser("manifest-key", help="Fetch the tenant's CI manifest HMAC key")
|
|
11663
11681
|
manifest_key.add_argument("--key", default=config.api_key())
|
|
11664
11682
|
|
|
11683
|
+
manifests = sub.add_parser("manifests", help="List, fetch, or submit CI attestation manifests")
|
|
11684
|
+
manifests_sub = manifests.add_subparsers(dest="manifests_cmd", required=True)
|
|
11685
|
+
ms_list = manifests_sub.add_parser("list", help="List repos with their latest manifest summary")
|
|
11686
|
+
ms_list.add_argument("--key", default=config.api_key())
|
|
11687
|
+
ms_get = manifests_sub.add_parser("get", help="Fetch the full manifest + drift for a repo")
|
|
11688
|
+
ms_get.add_argument("repo_id")
|
|
11689
|
+
ms_get.add_argument("--key", default=config.api_key())
|
|
11690
|
+
ms_push = manifests_sub.add_parser("push", help="Submit a signed manifest produced by `writ scan --manifest-out`")
|
|
11691
|
+
ms_push.add_argument("file")
|
|
11692
|
+
ms_push.add_argument("--key", default=config.api_key())
|
|
11693
|
+
|
|
11694
|
+
github = sub.add_parser("github", help="Manage the Writ GitHub App integration")
|
|
11695
|
+
github_sub = github.add_subparsers(dest="github_cmd", required=True)
|
|
11696
|
+
gh_inst = github_sub.add_parser("installations", help="List connected installations")
|
|
11697
|
+
gh_inst.add_argument("--key", default=config.api_key())
|
|
11698
|
+
gh_repos = github_sub.add_parser("repos", help="List repos and latest scan summaries")
|
|
11699
|
+
gh_repos.add_argument("--key", default=config.api_key())
|
|
11700
|
+
gh_repo = github_sub.add_parser("repo", help="Get a repo's latest findings")
|
|
11701
|
+
gh_repo.add_argument("repo_id")
|
|
11702
|
+
gh_repo.add_argument("--key", default=config.api_key())
|
|
11703
|
+
gh_thresh = github_sub.add_parser("threshold", help="Set/clear the per-installation check-run fail threshold")
|
|
11704
|
+
gh_thresh.add_argument("installation_id")
|
|
11705
|
+
gh_thresh.add_argument("threshold", nargs="?", default=None,
|
|
11706
|
+
help="Ungated count that flips the check run to failure; omit to clear")
|
|
11707
|
+
gh_thresh.add_argument("--key", default=config.api_key())
|
|
11708
|
+
gh_pc = github_sub.add_parser("pr-comments", help="Enable or disable PR comments for an installation")
|
|
11709
|
+
gh_pc.add_argument("installation_id")
|
|
11710
|
+
gh_pc.add_argument("enabled", choices=["on", "off"])
|
|
11711
|
+
gh_pc.add_argument("--key", default=config.api_key())
|
|
11712
|
+
gh_rescan = github_sub.add_parser("rescan", help="Rescan a repository now")
|
|
11713
|
+
gh_rescan.add_argument("repo_id")
|
|
11714
|
+
gh_rescan.add_argument("--totp-code", default="", help="TOTP step-up code")
|
|
11715
|
+
gh_rescan.add_argument("--key", default=config.api_key())
|
|
11716
|
+
gh_disc = github_sub.add_parser("disconnect", help="Disconnect a GitHub App installation")
|
|
11717
|
+
gh_disc.add_argument("installation_id")
|
|
11718
|
+
gh_disc.add_argument("--totp-code", default="", help="TOTP step-up code")
|
|
11719
|
+
gh_disc.add_argument("--key", default=config.api_key())
|
|
11720
|
+
|
|
11665
11721
|
args = parser.parse_args()
|
|
11666
11722
|
if args.cmd == "key":
|
|
11667
11723
|
payload = {"email": args.email}
|
|
@@ -11707,7 +11763,8 @@ def main():
|
|
|
11707
11763
|
return 0
|
|
11708
11764
|
|
|
11709
11765
|
_KEY_COMMANDS = {"me", "policy", "check", "receipts", "stream",
|
|
11710
|
-
"verify-chain", "invite", "token-verify", "manifest-key"
|
|
11766
|
+
"verify-chain", "invite", "token-verify", "manifest-key",
|
|
11767
|
+
"manifests", "github"}
|
|
11711
11768
|
if args.cmd in _KEY_COMMANDS and not _require_key(args.key):
|
|
11712
11769
|
return 2
|
|
11713
11770
|
|
|
@@ -11759,6 +11816,39 @@ def main():
|
|
|
11759
11816
|
return report_cmd(args)
|
|
11760
11817
|
if args.cmd == "manifest-key":
|
|
11761
11818
|
return _request_and_print("GET", "/v1/manifest-key", key=args.key)
|
|
11819
|
+
if args.cmd == "manifests":
|
|
11820
|
+
if args.manifests_cmd == "list":
|
|
11821
|
+
return _request_and_print("GET", "/v1/manifests", key=args.key)
|
|
11822
|
+
if args.manifests_cmd == "get":
|
|
11823
|
+
return _request_and_print("GET", "/v1/manifests/" + urllib.parse.quote(args.repo_id, safe=""), key=args.key)
|
|
11824
|
+
if args.manifests_cmd == "push":
|
|
11825
|
+
try:
|
|
11826
|
+
payload = _load_json_file(args.file)
|
|
11827
|
+
except (FileNotFoundError, ValueError) as exc:
|
|
11828
|
+
print("error: %s" % exc, file=sys.stderr)
|
|
11829
|
+
return 2
|
|
11830
|
+
return _request_and_print("POST", "/v1/manifests", payload, key=args.key)
|
|
11831
|
+
if args.cmd == "github":
|
|
11832
|
+
if args.github_cmd == "installations":
|
|
11833
|
+
return _request_and_print("GET", "/v1/github/installations", key=args.key)
|
|
11834
|
+
if args.github_cmd == "repos":
|
|
11835
|
+
return _request_and_print("GET", "/v1/github/repos", key=args.key)
|
|
11836
|
+
if args.github_cmd == "repo":
|
|
11837
|
+
return _request_and_print("GET", "/v1/github/repos/" + urllib.parse.quote(args.repo_id, safe=""), key=args.key)
|
|
11838
|
+
if args.github_cmd == "threshold":
|
|
11839
|
+
body = {"installationId": args.installation_id}
|
|
11840
|
+
if args.threshold is not None:
|
|
11841
|
+
body["threshold"] = int(args.threshold)
|
|
11842
|
+
return _request_and_print("POST", "/v1/github/check-fail-threshold", body, key=args.key)
|
|
11843
|
+
if args.github_cmd == "pr-comments":
|
|
11844
|
+
return _request_and_print("POST", "/v1/github/pr-comments",
|
|
11845
|
+
{"installationId": args.installation_id, "enabled": args.enabled == "on"}, key=args.key)
|
|
11846
|
+
if args.github_cmd == "rescan":
|
|
11847
|
+
return _request_and_print("POST", "/v1/github/repos/" + urllib.parse.quote(args.repo_id, safe="") + "/rescan",
|
|
11848
|
+
{"totpCode": args.totp_code}, key=args.key)
|
|
11849
|
+
if args.github_cmd == "disconnect":
|
|
11850
|
+
return _request_and_print("POST", "/v1/github/disconnect",
|
|
11851
|
+
{"installationId": args.installation_id, "totpCode": args.totp_code}, key=args.key)
|
|
11762
11852
|
if args.cmd in ("revoke", "reinstate", "revoked"):
|
|
11763
11853
|
if not _require_sponsor_token(args.sponsor_token):
|
|
11764
11854
|
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
|