Harden M9: Ed25519 attestation, full SHA-256 integrity, locked-by-default, prompt improvements, registry verification
This commit is contained in:
@@ -29,6 +29,12 @@ def cmd_registry(args):
|
||||
return _cmd_registry_update(args)
|
||||
elif args.registry_cmd == "publish":
|
||||
return _cmd_registry_publish(args)
|
||||
elif args.registry_cmd == "signing-key":
|
||||
return _cmd_registry_signing_key(args)
|
||||
elif args.registry_cmd == "improve":
|
||||
return _cmd_registry_improve(args)
|
||||
elif args.registry_cmd == "review-improvement":
|
||||
return _cmd_registry_review_improvement(args)
|
||||
elif args.registry_cmd == "update-readme":
|
||||
return _cmd_registry_update_readme(args)
|
||||
elif args.registry_cmd == "my-tools":
|
||||
@@ -51,6 +57,8 @@ def cmd_registry(args):
|
||||
print(" info <tool> Show tool information")
|
||||
print(" update Update local index cache")
|
||||
print(" publish [path] Publish a tool")
|
||||
print(" signing-key Manage Ed25519 release signing")
|
||||
print(" improve Submit a community improvement")
|
||||
print(" update-readme Update README for published tool(s)")
|
||||
print(" my-tools List your published tools")
|
||||
print(" status <tool> Check moderation status of a tool")
|
||||
@@ -598,6 +606,7 @@ def _cmd_registry_publish(args):
|
||||
from ..tool import load_tool, Tool, ToolStep
|
||||
|
||||
dep_result = None
|
||||
my_owner = ""
|
||||
try:
|
||||
client = get_client()
|
||||
|
||||
@@ -737,6 +746,7 @@ def _cmd_registry_publish(args):
|
||||
_print_quality_summary(preflight_result.get("quality"))
|
||||
if remote_report.get("errors"):
|
||||
return 1
|
||||
release_content_hash = preflight_result.get("content_hash", "")
|
||||
if sys.stdin.isatty() and not getattr(args, "force", False):
|
||||
try:
|
||||
if input("Publish this validated version? [y/N] ").strip().lower() != "y":
|
||||
@@ -751,7 +761,26 @@ def _cmd_registry_publish(args):
|
||||
try:
|
||||
client = get_client()
|
||||
owner = getattr(args, "owner", "")
|
||||
result = client.publish_tool(config_yaml, readme, defaults, owner=owner)
|
||||
attestation = None
|
||||
from ..signing import load_signing_key
|
||||
signing_key = load_signing_key()
|
||||
if signing_key:
|
||||
if not release_content_hash:
|
||||
print(
|
||||
"Registry did not return a content identity; refusing to sign.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
from ..attestation import sign_tool
|
||||
signer = owner or my_owner
|
||||
attestation = sign_tool(
|
||||
name, version, release_content_hash, signer, signing_key[0]
|
||||
).to_dict()
|
||||
result = client.publish_tool(
|
||||
config_yaml, readme, defaults, owner=owner,
|
||||
attestation=attestation,
|
||||
improvement_id=getattr(args, "improvement_id", None),
|
||||
)
|
||||
|
||||
pr_url = result.get("pr_url", "")
|
||||
status = result.get("status", "")
|
||||
@@ -826,6 +855,64 @@ def _cmd_registry_publish(args):
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_registry_signing_key(args):
|
||||
"""Initialize a local key and register its public half with the registry."""
|
||||
from ..signing import SIGNING_KEY_FILE, initialize_signing_key, load_signing_key
|
||||
from ..registry_client import RegistryError, get_client
|
||||
|
||||
if args.action == "status":
|
||||
key = load_signing_key()
|
||||
print(
|
||||
f"Signing key: {SIGNING_KEY_FILE}"
|
||||
if key else "No release signing key configured."
|
||||
)
|
||||
return 0 if key else 1
|
||||
try:
|
||||
_, public_key = initialize_signing_key()
|
||||
get_client().set_signing_public_key(public_key)
|
||||
except (OSError, ValueError, RegistryError) as exc:
|
||||
print(f"Could not initialize signing key: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"Release signing key initialized: {SIGNING_KEY_FILE}")
|
||||
print("The private key is stored locally with mode 0600; back it up securely.")
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_registry_improve(args):
|
||||
from ..registry_client import RegistryError, get_client
|
||||
if "/" not in args.tool:
|
||||
print("Tool must be specified as owner/name.", file=sys.stderr)
|
||||
return 2
|
||||
owner, name = args.tool.split("/", 1)
|
||||
try:
|
||||
proposed = Path(args.file).read_text(encoding="utf-8")
|
||||
result = get_client().submit_improvement(
|
||||
owner, name, args.version, args.step_index, proposed,
|
||||
args.rationale,
|
||||
)
|
||||
except (OSError, RegistryError) as exc:
|
||||
print(f"Could not submit improvement: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"Improvement {result.get('id')} tested and submitted for review.")
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_registry_review_improvement(args):
|
||||
from ..registry_client import RegistryError, get_client
|
||||
try:
|
||||
result = get_client().review_improvement(args.id, args.decision, args.notes)
|
||||
except RegistryError as exc:
|
||||
print(f"Could not review improvement: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"Improvement {args.id}: {result.get('status')}")
|
||||
if result.get("ready_to_apply"):
|
||||
print(
|
||||
"Publish the updated version with "
|
||||
f"--improvement-id {args.id} to apply credit and badges."
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_registry_update_readme(args):
|
||||
"""Update README for published tool(s) on the registry."""
|
||||
from ..registry_client import RegistryError, get_client
|
||||
|
||||
Reference in New Issue
Block a user