security: verify staged-secret enforcement across 22 workstation checkouts
This commit is contained in:
parent
2544b8703e
commit
355b6b9720
14 changed files with 949 additions and 16 deletions
222
docs/changes/CUST-WP-0080/activate_secret_hooks.py
Normal file
222
docs/changes/CUST-WP-0080/activate_secret_hooks.py
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Apply/verify or roll back the exact reviewed local hook proposal.
|
||||
|
||||
No production credential inputs, repository source/index writes, Git config
|
||||
changes or remote calls. Synthetic probes use alternate indexes/object stores.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import signal
|
||||
import stat
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
class Refused(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def digest(path):
|
||||
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def safe_regular(path):
|
||||
path = Path(path)
|
||||
if path.is_symlink() or not path.is_file(): raise Refused('file-shape-refused')
|
||||
if path.stat().st_uid != os.getuid(): raise Refused('file-owner-refused')
|
||||
return path
|
||||
|
||||
|
||||
def atomic(path, data, mode):
|
||||
path = Path(path)
|
||||
fd, temporary = tempfile.mkstemp(prefix='.hook-activation-', dir=path.parent)
|
||||
try:
|
||||
with os.fdopen(fd, 'wb') as output:
|
||||
output.write(data); output.flush(); os.fsync(output.fileno())
|
||||
os.chmod(temporary, mode)
|
||||
os.replace(temporary, path)
|
||||
finally:
|
||||
if os.path.exists(temporary): os.unlink(temporary)
|
||||
|
||||
|
||||
def run(args, cwd=None, env=None, payload=None):
|
||||
result = subprocess.run(args, cwd=cwd, env=env, input=payload, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, timeout=60, check=False)
|
||||
if result.returncode: raise Refused('preparation-command-failed')
|
||||
return result.stdout
|
||||
|
||||
|
||||
def clean_git_env():
|
||||
env = dict(os.environ)
|
||||
for key in ('GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_OBJECT_DIRECTORY',
|
||||
'GIT_ALTERNATE_OBJECT_DIRECTORIES'):
|
||||
env.pop(key, None)
|
||||
return env
|
||||
|
||||
|
||||
def route(root):
|
||||
env = clean_git_env()
|
||||
actual = run(['git', 'rev-parse', '--show-toplevel'], root, env).decode().strip()
|
||||
if actual != root: raise Refused('checkout-root-changed')
|
||||
name = run(['git', 'rev-parse', '--git-path', 'hooks/pre-commit'], root, env).decode().strip()
|
||||
path = Path(name)
|
||||
return str(path if path.is_absolute() else Path(root) / path)
|
||||
|
||||
|
||||
def validate(proposal, applied=False):
|
||||
if digest(__file__) != proposal['executor_sha256']:
|
||||
raise Refused('executor-revision-changed')
|
||||
expected = {row['repo']: row['effective_pre_commit'] for row in proposal['original_routes']}
|
||||
for root in proposal['scope_roots']:
|
||||
if route(root) != expected[Path(root).name]: raise Refused('hook-route-changed')
|
||||
for item in proposal['hooks']:
|
||||
safe_regular(item['path'])
|
||||
field = 'after_sha256' if applied else 'before_sha256'
|
||||
if digest(item['path']) != item[field]: raise Refused('hook-revision-changed')
|
||||
if digest(item['backup']) != item['before_sha256']: raise Refused('backup-changed')
|
||||
for item in proposal['protected_files']:
|
||||
if digest(item['path']) != item['sha256']: raise Refused('dependency-or-other-hook-changed')
|
||||
safe_regular(proposal['hook_source'])
|
||||
if digest(proposal['hook_source']) != proposal['hooks'][0]['after_sha256']:
|
||||
raise Refused('candidate-hook-changed')
|
||||
binary = proposal['binary']
|
||||
if digest(safe_regular(binary['source'])) != binary['sha256']:
|
||||
raise Refused('scanner-source-changed')
|
||||
target = Path(binary['target'])
|
||||
for ancestor in (target, *target.parents):
|
||||
if ancestor.is_symlink(): raise Refused('scanner-path-redirected')
|
||||
if target.exists() and digest(safe_regular(target)) != binary['sha256']:
|
||||
raise Refused('scanner-target-conflict')
|
||||
|
||||
|
||||
def probe(proposal):
|
||||
results = []
|
||||
scanner = Path(proposal['binary']['target'])
|
||||
original_mode = stat.S_IMODE(scanner.stat().st_mode)
|
||||
with tempfile.TemporaryDirectory(prefix='rmgr-hook-proof-') as temporary:
|
||||
area = Path(temporary)
|
||||
key = area / 'disposable-key'
|
||||
run(['ssh-keygen', '-q', '-t', 'ed25519', '-N', '', '-f', str(key)])
|
||||
synthetic = key.read_bytes()
|
||||
cases = []
|
||||
for number, root in enumerate(proposal['scope_roots']):
|
||||
local = area / str(number); local.mkdir(); objects = local / 'objects'; objects.mkdir()
|
||||
env = clean_git_env()
|
||||
original_objects = run(['git', 'rev-parse', '--git-path', 'objects'], root, env).decode().strip()
|
||||
if not Path(original_objects).is_absolute(): original_objects = str(Path(root) / original_objects)
|
||||
env.update(GIT_INDEX_FILE=str(local / 'index'), GIT_OBJECT_DIRECTORY=str(objects),
|
||||
GIT_ALTERNATE_OBJECT_DIRECTORIES=original_objects)
|
||||
hook = route(root)
|
||||
def stage(data, name='__rmgr_security_synthetic_probe__.txt'):
|
||||
run(['git', 'read-tree', 'HEAD'], root, env)
|
||||
oid = run(['git', 'hash-object', '-w', '--stdin'], root, env, data).decode().strip()
|
||||
run(['git', 'update-index', '--add', '--cacheinfo', '100644,' + oid + ',' + name], root, env)
|
||||
def invoke():
|
||||
result = subprocess.run([hook], cwd=root, env=env, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, timeout=60, check=False)
|
||||
if synthetic.strip() in result.stdout + result.stderr:
|
||||
raise Refused('synthetic-content-disclosed')
|
||||
return result
|
||||
stage(b'print("synthetic clean input")\n')
|
||||
clean = invoke()
|
||||
if clean.returncode != 0 or json.loads(clean.stdout).get('status') != 'clean':
|
||||
raise Refused('clean-native-probe-failed')
|
||||
stage(synthetic)
|
||||
blocked = invoke()
|
||||
if blocked.returncode != 1 or json.loads(blocked.stdout).get('status') != 'blocked':
|
||||
raise Refused('secret-native-probe-failed')
|
||||
stage(b'synthetic generated-only change\n', '.custodian-brief.md')
|
||||
generated = invoke()
|
||||
if generated.returncode != 1 or b'Generated-only commits rejected' not in generated.stderr:
|
||||
raise Refused('generated-guard-native-probe-failed')
|
||||
stage(b'print("synthetic clean input")\n')
|
||||
cases.append((root, hook, env))
|
||||
results.append({'repo': Path(root).name, 'clean_exit': 0, 'secret_exit': 1,
|
||||
'generated_only_exit': 1, 'hook_sha256': digest(hook)})
|
||||
# Brief, reviewed failure injection; restore even on SIGTERM/INT/HUP.
|
||||
try:
|
||||
scanner.chmod(0o644)
|
||||
for row, (root, hook, env) in zip(results, cases):
|
||||
result = subprocess.run([hook], cwd=root, env=env, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, timeout=60, check=False)
|
||||
body = json.loads(result.stdout)
|
||||
if result.returncode != 2 or body.get('status') != 'unverified':
|
||||
raise Refused('scanner-failure-native-probe-failed')
|
||||
row['scanner_failure_exit'] = 2
|
||||
row['scanner_failure_reason'] = body.get('reason')
|
||||
# An unreviewed checkout keeps its existing generated-only behavior.
|
||||
outside = area / 'outside-profile'; outside.mkdir()
|
||||
env = clean_git_env()
|
||||
run(['git', 'init', '-q'], outside, env)
|
||||
(outside / 'source.py').write_text('print("clean outside profile")\n')
|
||||
run(['git', 'add', 'source.py'], outside, env)
|
||||
path = run(['git', 'rev-parse', '--git-path', 'hooks/pre-commit'], outside, env).decode().strip()
|
||||
result = subprocess.run([path], cwd=outside, env=env, capture_output=True, timeout=30, check=False)
|
||||
if result.returncode != 0: raise Refused('unreviewed-checkout-behavior-changed')
|
||||
finally:
|
||||
scanner.chmod(original_mode)
|
||||
return {'repositories': results, 'unreviewed_checkout_preserved': True,
|
||||
'indexes_and_objects': 'temporary alternate stores only',
|
||||
'scanner_mode_restored': stat.S_IMODE(scanner.stat().st_mode) == original_mode}
|
||||
|
||||
|
||||
def restore(proposal):
|
||||
for item in proposal['hooks']:
|
||||
current = digest(safe_regular(item['path']))
|
||||
if current not in (item['before_sha256'], item['after_sha256']):
|
||||
raise Refused('rollback-target-changed')
|
||||
if digest(item['backup']) != item['before_sha256']: raise Refused('backup-changed')
|
||||
for item in proposal['hooks']:
|
||||
atomic(item['path'], Path(item['backup']).read_bytes(), item['before_mode'])
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--proposal', type=Path, required=True)
|
||||
parser.add_argument('--receipt', type=Path, required=True)
|
||||
parser.add_argument('--apply', action='store_true')
|
||||
parser.add_argument('--rollback', action='store_true')
|
||||
args = parser.parse_args()
|
||||
proposal = json.loads(args.proposal.read_text())
|
||||
if args.rollback:
|
||||
restore(proposal)
|
||||
print('Restored exact prior hooks; Git routing unchanged.')
|
||||
return
|
||||
validate(proposal)
|
||||
if not args.apply:
|
||||
print('Proposal hashes, dependencies, backups and exact checkout routes verified.')
|
||||
return
|
||||
changed = False
|
||||
receipt = {'proposal_id': proposal['proposal_id'], 'proposal_sha256': digest(args.proposal),
|
||||
'executor_sha256': digest(__file__), 'result': 'unverified', 'cost_eur': 0}
|
||||
try:
|
||||
target = Path(proposal['binary']['target'])
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not target.exists(): atomic(target, Path(proposal['binary']['source']).read_bytes(), 0o755)
|
||||
for item in proposal['hooks']:
|
||||
changed = True
|
||||
atomic(item['path'], Path(proposal['hook_source']).read_bytes(), 0o755)
|
||||
validate(proposal, applied=True)
|
||||
receipt['verification'] = probe(proposal)
|
||||
validate(proposal, applied=True)
|
||||
receipt['result'] = 'verified'
|
||||
except BaseException:
|
||||
if changed:
|
||||
restore(proposal)
|
||||
receipt['rollback'] = 'prior hooks restored'
|
||||
raise
|
||||
finally:
|
||||
args.receipt.write_text(json.dumps(receipt, indent=2) + '\n')
|
||||
print(f"Verified local scanner enforcement for {len(proposal['scope_roots'])} scoped checkouts; no Git routing changed.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def interrupted(*_): raise Refused('interrupted')
|
||||
for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP): signal.signal(sig, interrupted)
|
||||
try:
|
||||
main()
|
||||
except (Refused, OSError, ValueError, subprocess.SubprocessError):
|
||||
raise SystemExit('Activation refused or failed; inspect the value-free receipt and exact rollback state.')
|
||||
9
docs/changes/CUST-WP-0080/secret-hooks-pre-commit
Normal file
9
docs/changes/CUST-WP-0080/secret-hooks-pre-commit
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
# CUST-WP-0080 / RMGR-WP-0021: preserve generated-only guard and exact local scan scope.
|
||||
set -eu
|
||||
python3 /home/worsch/the-custodian/scripts/check_generated_only_commit.py --staged
|
||||
case "$(git rev-parse --show-toplevel)" in
|
||||
/home/worsch/access-engine|/home/worsch/approval-engine|/home/worsch/audit-core|/home/worsch/gate-house|/home/worsch/glas-harness|/home/worsch/key-cape|/home/worsch/kings-guard|/home/worsch/maturity-engine|/home/worsch/net-kingdom|/home/worsch/ops-mason|/home/worsch/ops-warden|/home/worsch/policy-nexus|/home/worsch/railiance-forge|/home/worsch/railiance-platform|/home/worsch/repo-manager|/home/worsch/role-engine|/home/worsch/secrets-engine|/home/worsch/security-canon|/home/worsch/tenant-engine|/home/worsch/the-custodian|/home/worsch/user-engine|/home/worsch/zone-engine)
|
||||
exec python3 /home/worsch/repo-manager/src/repo_manager/secret_scan.py --path . --binary /home/worsch/.local/lib/repo-manager/gitleaks/8.30.1/gitleaks
|
||||
;;
|
||||
esac
|
||||
4
docs/changes/CUST-WP-0080/secret-hooks-prior-hook-0
Normal file
4
docs/changes/CUST-WP-0080/secret-hooks-prior-hook-0
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
# Custodian shared generated-commit guard; preserve other hooks.
|
||||
set -eu
|
||||
exec python3 /home/worsch/the-custodian/scripts/check_generated_only_commit.py --staged
|
||||
5
docs/changes/CUST-WP-0080/secret-hooks-prior-hook-1
Normal file
5
docs/changes/CUST-WP-0080/secret-hooks-prior-hook-1
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
# Install as .git/hooks/pre-commit; rejects generated/evidence-only auto commits too.
|
||||
set -eu
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
exec python3 "$REPO_ROOT/scripts/check_generated_only_commit.py" --staged
|
||||
329
docs/changes/CUST-WP-0080/secret-hooks-proposal.json
Normal file
329
docs/changes/CUST-WP-0080/secret-hooks-proposal.json
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
{
|
||||
"proposal_id": "CUST80-G07-local-hooks-001",
|
||||
"scope_roots": [
|
||||
"/home/worsch/access-engine",
|
||||
"/home/worsch/approval-engine",
|
||||
"/home/worsch/audit-core",
|
||||
"/home/worsch/gate-house",
|
||||
"/home/worsch/glas-harness",
|
||||
"/home/worsch/key-cape",
|
||||
"/home/worsch/kings-guard",
|
||||
"/home/worsch/maturity-engine",
|
||||
"/home/worsch/net-kingdom",
|
||||
"/home/worsch/ops-mason",
|
||||
"/home/worsch/ops-warden",
|
||||
"/home/worsch/policy-nexus",
|
||||
"/home/worsch/railiance-forge",
|
||||
"/home/worsch/railiance-platform",
|
||||
"/home/worsch/repo-manager",
|
||||
"/home/worsch/role-engine",
|
||||
"/home/worsch/secrets-engine",
|
||||
"/home/worsch/security-canon",
|
||||
"/home/worsch/tenant-engine",
|
||||
"/home/worsch/the-custodian",
|
||||
"/home/worsch/user-engine",
|
||||
"/home/worsch/zone-engine"
|
||||
],
|
||||
"binary": {
|
||||
"source": "/tmp/netkingdom-secret-detection/gitleaks",
|
||||
"target": "/home/worsch/.local/lib/repo-manager/gitleaks/8.30.1/gitleaks",
|
||||
"sha256": "88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509"
|
||||
},
|
||||
"hook_source": "/home/worsch/the-custodian/docs/changes/CUST-WP-0080/secret-hooks-pre-commit",
|
||||
"hooks": [
|
||||
{
|
||||
"path": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"before_sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"before_mode": 493,
|
||||
"backup": "/home/worsch/the-custodian/docs/changes/CUST-WP-0080/secret-hooks-prior-hook-0",
|
||||
"after_sha256": "d50cf763dc9814383c6886e30b6650bbbdcb49ddd78889082e3fda3626a2cbfd"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/pre-commit",
|
||||
"before_sha256": "4d23af188d7891209f032123508893d2bc49a1709644278a0f1fa9008b0b187f",
|
||||
"before_mode": 493,
|
||||
"backup": "/home/worsch/the-custodian/docs/changes/CUST-WP-0080/secret-hooks-prior-hook-1",
|
||||
"after_sha256": "d50cf763dc9814383c6886e30b6650bbbdcb49ddd78889082e3fda3626a2cbfd"
|
||||
}
|
||||
],
|
||||
"original_routes": [
|
||||
{
|
||||
"repo": "security-canon",
|
||||
"head": "1664a6def20be2759a1e575024563efd27c9f36f",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "net-kingdom",
|
||||
"head": "19a1c75d309991de8654c4b7a9eeffea8fb90b18",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "gate-house",
|
||||
"head": "bc890c4d050a2e726ce8988a8260ab9caea133e3",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "ops-mason",
|
||||
"head": "d86ad141befb6800468f5132d4c7c9ce2f852bb5",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "ops-warden",
|
||||
"head": "6d2f1be9691b19e826898a2166ae4451e4802ae2",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "key-cape",
|
||||
"head": "1344fc80e56315c5c99f58993f9ee02ce79e8a76",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "user-engine",
|
||||
"head": "85a419d6c5d0b4434d3ca7f33bde3092f5838f79",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "tenant-engine",
|
||||
"head": "1dabbcf47d87d27d118dcf3147d9e3588a807e0c",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "role-engine",
|
||||
"head": "d231241dbfeeb541b34243f4022883aa6417a1eb",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "zone-engine",
|
||||
"head": "b5a6f88a82b125ebc82c0484bec1ea541e4f95d0",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "access-engine",
|
||||
"head": "16ca97b2776789f730cc7049a2d6533612faaccd",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "approval-engine",
|
||||
"head": "e1649617e8ad321f79632d5fd33e973ae5d5ae51",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "secrets-engine",
|
||||
"head": "d379a3dfba73c42cd97f4d9cba3a02d705155e4d",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "audit-core",
|
||||
"head": "f1e715326b3ffd956623a84fa7ab0ce5ab3fbcf9",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "kings-guard",
|
||||
"head": "af02e2547598d6ec0e83f482d9ea3ef943ea67b7",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "maturity-engine",
|
||||
"head": "2f1de542cc8f24a79452f906d99f95be39d8b6a9",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "policy-nexus",
|
||||
"head": "f784f017b0822f34c785f0b34c7c55fcf39c9d99",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"head": "7c79293831e2f46ad93f2ef689d9129b2ac0e139",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "glas-harness",
|
||||
"head": "5ce4f38aedfd27cc783131f18480df02e2951514",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "repo-manager",
|
||||
"head": "78d91efc76ddaf778365e445dc8fb5b6c924a274",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-forge",
|
||||
"head": "e395e5e9c196fe7cea9cd2be513554e493e675ee",
|
||||
"effective_pre_commit": "/home/worsch/repo-manager/.githooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "03a46897ea61589fe111316e561a98b423028375a1cddd1152b808df1fbeb4b5",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
},
|
||||
{
|
||||
"repo": "the-custodian",
|
||||
"head": "e2dbe1267ffea38cd8042c153c942b26f6d177a4",
|
||||
"effective_pre_commit": "/home/worsch/the-custodian/.git/hooks/pre-commit",
|
||||
"executable": true,
|
||||
"sha256": "4d23af188d7891209f032123508893d2bc49a1709644278a0f1fa9008b0b187f",
|
||||
"observed_control": "generated-only commit guard; no scanner invocation",
|
||||
"scanner_activation": "not activated by this repair"
|
||||
}
|
||||
],
|
||||
"expected_result": "Synthetic clean source accepted, synthetic private key rejected, unavailable scanner refused on all 22 exact roots; generated-only guard/provenance and routing preserved.",
|
||||
"verification": "Alternate Git indexes and temporary object directories; no user index, source file, branch or production credential mutation. Brief native scanner non-executable probe restores mode in finally.",
|
||||
"rollback": "Restore exact prior hook bytes/modes after checking installed hashes; leave admitted scanner inert. No Git configuration changed.",
|
||||
"cost_eur": 0,
|
||||
"risk": "Local commits to selected checkouts may be refused on detections or verification failure. Scanner mode fault probe briefly refuses concurrent commits; no runtime service changes.",
|
||||
"protected_files": [
|
||||
{
|
||||
"path": "/home/worsch/repo-manager/src/repo_manager/secret_scan.py",
|
||||
"sha256": "4559d7714308eaa70ce5f8fa91dfcc6b10fe74b607e9f2a94269d31ff76bce9d"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/scripts/check_generated_only_commit.py",
|
||||
"sha256": "1b372740a3265bbaaef254582be3eb116a8659f2efffccd5a26ec1c9d8db7105"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/repo-manager/.githooks/prepare-commit-msg",
|
||||
"sha256": "6640924b9ba281e1fc413802cff91c808580b323f11e60d1fc71f4d5cd13bd45"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/pre-rebase.sample",
|
||||
"sha256": "4febce867790052338076f4e66cc47efb14879d18097d1d61c8261859eaaa7b3"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/fsmonitor-watchman.sample",
|
||||
"sha256": "e0549964e93897b519bd8e333c037e51fff0f88ba13e086a331592bf801fa1d0"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/update.sample",
|
||||
"sha256": "8d5f2fa83e103cf08b57eaa67521df9194f45cbdbcb37da52ad586097a14d106"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/post-update.sample",
|
||||
"sha256": "81765af2daef323061dcbc5e61fc16481cb74b3bac9ad8a174b186523586f6c5"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/push-to-checkout.sample",
|
||||
"sha256": "a53d0741798b287c6dd7afa64aee473f305e65d3f49463bb9d7408ec3b12bf5f"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/pre-applypatch.sample",
|
||||
"sha256": "e15c5b469ea3e0a695bea6f2c82bcf8e62821074939ddd85b77e0007ff165475"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/post-commit",
|
||||
"sha256": "da2461970c32ee3ca9be48993a15476e368184622b008732c202b9c8a35840e6"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/pre-merge-commit.sample",
|
||||
"sha256": "d3825a70337940ebbd0a5c072984e13245920cdf8898bd225c8d27a6dfc9cb53"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/commit-msg.sample",
|
||||
"sha256": "1f74d5e9292979b573ebd59741d46cb93ff391acdd083d340b94370753d92437"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/pre-push.sample",
|
||||
"sha256": "ecce9c7e04d3f5dd9d8ada81753dd1d549a9634b26770042b58dda00217d086a"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/prepare-commit-msg.sample",
|
||||
"sha256": "e9ddcaa4189fddd25ed97fc8c789eca7b6ca16390b2392ae3276f0c8e1aa4619"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/pre-commit.sample",
|
||||
"sha256": "f9af7d95eb1231ecf2eba9770fedfa8d4797a12b02d7240e98d568201251244a"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/sendemail-validate.sample",
|
||||
"sha256": "44ebfc923dc5466bc009602f0ecf067b9c65459abfe8868ddc49b78e6ced7a92"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/pre-receive.sample",
|
||||
"sha256": "a4c3d2b9c7bb3fd8d1441c31bd4ee71a595d66b44fcf49ddb310252320169989"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.git/hooks/applypatch-msg.sample",
|
||||
"sha256": "0223497a0b8b033aa58a3a521b8629869386cf7ab0e2f101963d328aa62193f7"
|
||||
}
|
||||
],
|
||||
"executor_sha256": "1935cc442026a36db8fecc7f58f779dc877a79b6b2e987f8ab074138230ff74f"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue