Skip to content

Commit 091e2fa

Browse files
committed
sync(grant): scrub_dsstore + can-6 remediation from orama #260
Mirror guard-sync manifest, scrub_dsstore.sh, grant v2 hardening, expanded tests, githooks -x guard, and WORKSPACE markdownlint fixes.
1 parent fd08ef1 commit 091e2fa

11 files changed

Lines changed: 232 additions & 107 deletions

.agent/memory/working/WORKSPACE.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ grant stack synced from orama via `sync-attribution-guard-scripts.sh` after each
1616

1717
## Saga doc (read this first)
1818

19-
`PR_BODY_GRANT_HMAC_MVP_SAGA_2026-08-02.md` — timeline, research, decisions D1–D17, replay state machine, operator workflow, tips.
19+
`PR_BODY_GRANT_HMAC_MVP_SAGA_2026-08-02.md` — timeline, research, decisions D1–D17,
20+
replay state machine, operator workflow, tips.
2021

2122
## Canonical artifacts
2223

@@ -37,14 +38,15 @@ bash scripts/cursor/grant-pr-body-human-override.sh owner/repo N --file follow-u
3738
bash scripts/cursor/append-pr-body.sh owner/repo N --file follow-up.md
3839
```
3940

40-
Grant lifecycle: **mint → reserve → gh edit → mark-applied → consume**. Re-run append reconciles if follow-up already on remote (crash recovery).
41+
Grant lifecycle: **mint → reserve → gh edit → mark-applied → consume**.
42+
Re-run append reconciles if follow-up already on remote (crash recovery).
4143

4244
## Verification (last run 2026-08-02)
4345

4446
```bash
4547
python3 -m pytest tests/test_pr_body_grant_lib.py tests/test_append_pr_body_grant_flow.py \
4648
tests/test_pr_body_guard_core.py tests/test_check_guard_sync_divergence.py -q
47-
# Result: 21 passed (orama + PT after sync)
49+
# Result: 26 passed (orama + PT after sync)
4850
```
4951

5052
## Next

.githooks/pre-commit

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,31 @@
22
set -euo pipefail
33
ROOT="$(git rev-parse --show-toplevel)"
44

5-
"$ROOT/scripts/git/ensure_hooks_installed.sh"
5+
# Guard: scrub macOS .DS_Store from .git/ internals before hygiene runs.
6+
# .gitignore cannot cover .git/; Finder drops .DS_Store here and it trips
7+
# repo_hygiene's "metadata inside git refs" gate. Best-effort, non-blocking.
8+
if [[ -x "$ROOT/scripts/git/scrub_dsstore.sh" ]]; then
9+
bash "$ROOT/scripts/git/scrub_dsstore.sh" || true
10+
fi
611

7-
# Local-runtime overlay: block committing discovery-written LAN IPs in config YAML.
8-
export PYTHONUTF8=1
9-
export PYTHONIOENCODING=utf-8
10-
for py in \
11-
"$ROOT/.venv/Scripts/python.exe" \
12-
"$ROOT/.venv/bin/python" \
13-
python3 \
14-
python; do
15-
if command -v "$py" >/dev/null 2>&1 && "$py" -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 9) else 1)" >/dev/null 2>&1; then
16-
if "$py" "$ROOT/scripts/git/check_local_runtime_overlay.py" "$ROOT"; then
17-
:
18-
else
12+
# Fast-fail: private LAN IPs in tracked config (3080/5080 endpoints → env vars).
13+
if [ -f "$ROOT/scripts/hooks/no_committed_lan_topology.py" ]; then
14+
if ! command -v python3 >/dev/null 2>&1; then
15+
echo "ERROR: python3 required for LAN topology pre-commit gate" >&2
1916
exit 1
2017
fi
21-
break
22-
fi
23-
done
18+
python3 "$ROOT/scripts/hooks/no_committed_lan_topology.py" || exit 1
19+
fi
2420

25-
# Full hygiene gate — same checks as CI (identity, forbidden tokens, paths, etc.).
26-
export PYTHONUTF8=1
27-
export PYTHONIOENCODING=utf-8
28-
if [[ -f "$ROOT/scripts/review/repo_hygiene.py" ]]; then
29-
for py in \
30-
"$ROOT/.venv/Scripts/python.exe" \
31-
"$ROOT/.venv/bin/python" \
32-
python3 \
33-
python; do
34-
if command -v "$py" >/dev/null 2>&1 && "$py" -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 9) else 1)" >/dev/null 2>&1; then
35-
exec "$py" "$ROOT/scripts/review/repo_hygiene.py" "$ROOT"
36-
fi
37-
done
38-
echo "[pre-commit] repo_hygiene.py requires Python >= 3.9; none found on PATH" >&2
39-
exit 1
21+
# Canonical hygiene gate — the SAME check CI runs (scripts/review/repo_hygiene.py):
22+
# commit identity, forbidden tokens, workstation/personal paths, secrets, bidi
23+
# controls, markdown-link hygiene, etc. Running it here catches a leak BEFORE it
24+
# enters history, so the expunge-git scrub is a last resort, not the routine
25+
# (#1802 follow-up). repo_hygiene.py subsumes check_identity (kept in sync), so
26+
# this preserves the prior identity gate and adds the rest.
27+
if [ -f "$ROOT/scripts/review/repo_hygiene.py" ] && command -v python3 >/dev/null 2>&1; then
28+
exec python3 "$ROOT/scripts/review/repo_hygiene.py" "$ROOT"
4029
fi
4130

31+
# Fallback for clones that predate the hygiene script: identity check only.
4232
exec "$ROOT/scripts/git/check_identity.sh"

.githooks/pre-push

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ trap 'rm -f "$push_refs"' EXIT
1111
cat >"$push_refs"
1212

1313
# Guard: scrub macOS .DS_Store from .git/ internals (see scrub_dsstore.sh).
14-
bash "$ROOT/scripts/git/scrub_dsstore.sh" || true
14+
if [[ -x "$ROOT/scripts/git/scrub_dsstore.sh" ]]; then
15+
bash "$ROOT/scripts/git/scrub_dsstore.sh" || true
16+
fi
1517

1618
# Guard: never push while a --no-commit merge/cherry-pick/revert is still
1719
# uncommitted (see check_no_pending_merge.sh for the incident this prevents).
@@ -25,8 +27,9 @@ range_for_ref() {
2527
local local_oid="$1"
2628
local remote_oid="$2"
2729
if [[ "$remote_oid" == "$zero" ]]; then
28-
if git rev-parse --verify "$upstream_ref" >/dev/null 2>&1; then
29-
echo "$(git rev-parse "$upstream_ref")..${local_oid}"
30+
local upstream_oid
31+
if upstream_oid="$(git rev-parse --verify "$upstream_ref" 2>/dev/null)"; then
32+
printf '%s..%s\n' "$upstream_oid" "$local_oid"
3033
else
3134
echo "${local_oid}"
3235
fi

scripts/cursor/append-pr-body.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,9 @@ fi
255255

256256
consume_cmd=(python3 "$GRANT_LIB" consume "${grant_append_args[@]}")
257257
if ! "${consume_cmd[@]}"; then
258-
echo "error: grant consume failed after PR body update — treat as security incident" >&2
258+
echo "error: grant consume failed AFTER the PR body update. The remote write already landed." >&2
259+
echo " cause: the nonce ledger could not be updated, so the grant may still be replayable." >&2
260+
echo " fix: delete ~/.cursor/pr-body-human-override-ack now, then review .git/pr-body-backups." >&2
259261
exit 1
260262
fi
261263

scripts/cursor/grant-pr-body-human-override.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ if [[ -n "${CURSOR_AGENT:-}" ]] || [[ -n "${CI:-}" ]]; then
1616
exit 1
1717
fi
1818

19+
if [[ $# -lt 2 && "${1:-}" != "-h" && "${1:-}" != "--help" ]]; then
20+
echo "error: usage: grant-pr-body-human-override.sh <owner/repo> <pr-number> --file|--message" >&2
21+
exit 1
22+
fi
23+
1924
repo_slug="${1:-}"
2025
pr_number="${2:-}"
2126
shift 2 || true

scripts/cursor/hooks/pr-body-guard-core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def _shell_decision_lines(command_line: str) -> list[str]:
164164
backup_lines.extend(backups)
165165

166166
if backup_lines:
167-
return backup_lines + ["ALLOW"]
167+
return [*backup_lines, "ALLOW"]
168168
return ["ALLOW"]
169169

170170

scripts/cursor/pr-body-grant-lib.py

Lines changed: 58 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import secrets
3333
import subprocess
3434
import sys
35+
from contextlib import contextmanager
3536
from datetime import datetime, timezone
3637
from pathlib import Path
3738
from typing import Any
@@ -174,12 +175,24 @@ def _read_fallback_secret_file() -> str | None:
174175
return None
175176

176177

177-
def _write_fallback_secret_file(secret: str) -> None:
178-
FALLBACK_SECRET_PATH.parent.mkdir(parents=True, exist_ok=True)
179-
fd = os.open(str(FALLBACK_SECRET_PATH), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
178+
def _validate_repo_slug(repo: str) -> None:
179+
if "|" in repo or not repo.strip():
180+
raise GrantError("grant repo must not contain pipe characters")
181+
182+
183+
def _write_private_file(path: Path, content: str) -> None:
184+
path.parent.mkdir(parents=True, exist_ok=True)
185+
path.unlink(missing_ok=True)
186+
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
187+
if hasattr(os, "O_NOFOLLOW"):
188+
flags |= os.O_NOFOLLOW
189+
fd = os.open(str(path), flags, 0o600)
180190
with os.fdopen(fd, "w", encoding="utf-8") as handle:
181-
handle.write(secret)
182-
handle.write("\n")
191+
handle.write(content)
192+
193+
194+
def _write_fallback_secret_file(secret: str) -> None:
195+
_write_private_file(FALLBACK_SECRET_PATH, secret + "\n")
183196

184197

185198
def resolve_hmac_secret(allow_generate: bool = False) -> bytes:
@@ -251,24 +264,29 @@ def _grant_ttl_ok(issued_raw: str) -> bool:
251264
return 0 <= age <= GRANT_TTL_SECONDS
252265

253266

254-
def _lock_nonce_state() -> tuple[Any, dict[str, Any]]:
267+
@contextmanager
268+
def _locked_nonce_state():
255269
NONCE_STATE_PATH.parent.mkdir(parents=True, exist_ok=True)
256270
handle = open(NONCE_STATE_PATH, "a+", encoding="utf-8")
257-
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
258-
handle.seek(0)
259-
raw = handle.read()
260-
if raw.strip():
261-
try:
262-
state = json.loads(raw)
263-
except json.JSONDecodeError:
271+
try:
272+
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
273+
handle.seek(0)
274+
raw = handle.read()
275+
if raw.strip():
276+
try:
277+
state = json.loads(raw)
278+
except json.JSONDecodeError:
279+
state = {"nonces": {}}
280+
else:
264281
state = {"nonces": {}}
265-
else:
266-
state = {"nonces": {}}
267-
if "nonces" not in state or not isinstance(state["nonces"], dict):
268-
state["nonces"] = {}
269-
if "reservations" not in state or not isinstance(state.get("reservations"), dict):
270-
state["reservations"] = {}
271-
return handle, state
282+
if "nonces" not in state or not isinstance(state["nonces"], dict):
283+
state["nonces"] = {}
284+
if "reservations" not in state or not isinstance(state.get("reservations"), dict):
285+
state["reservations"] = {}
286+
yield handle, state
287+
finally:
288+
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
289+
handle.close()
272290

273291

274292
def _write_nonce_state(handle: Any, state: dict[str, Any]) -> None:
@@ -318,18 +336,14 @@ def _reservation_blocks_verify(
318336
pr_number: str,
319337
content_digest: str,
320338
) -> tuple[bool, str]:
321-
handle, state = _lock_nonce_state()
322-
try:
339+
with _locked_nonce_state() as (_handle, state):
323340
_prune_nonce_state(state)
324341
entry = state.get("reservations", {}).get(nonce)
325342
if not entry:
326343
return True, ""
327344
if _reservation_matches(entry, repo, pr_number, content_digest):
328345
return True, ""
329346
return False, "grant nonce reserved for a different append operation"
330-
finally:
331-
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
332-
handle.close()
333347

334348

335349
def reserve_nonce_atomic(
@@ -339,8 +353,7 @@ def reserve_nonce_atomic(
339353
content_digest: str,
340354
) -> tuple[bool, str]:
341355
"""Reserve nonce before remote mutation. Idempotent for the same binding."""
342-
handle, state = _lock_nonce_state()
343-
try:
356+
with _locked_nonce_state() as (handle, state):
344357
_prune_nonce_state(state)
345358
if nonce in state["nonces"]:
346359
return False, "grant nonce already consumed (replay blocked)"
@@ -359,15 +372,11 @@ def reserve_nonce_atomic(
359372
}
360373
_write_nonce_state(handle, state)
361374
return True, ""
362-
finally:
363-
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
364-
handle.close()
365375

366376

367377
def mark_remote_applied_atomic(nonce: str) -> tuple[bool, str]:
368378
"""Mark remote PR body mutation successful; required before consume."""
369-
handle, state = _lock_nonce_state()
370-
try:
379+
with _locked_nonce_state() as (handle, state):
371380
_prune_nonce_state(state)
372381
reservations = state["reservations"]
373382
entry = reservations.get(nonce)
@@ -376,39 +385,27 @@ def mark_remote_applied_atomic(nonce: str) -> tuple[bool, str]:
376385
entry["remote_applied"] = True
377386
_write_nonce_state(handle, state)
378387
return True, ""
379-
finally:
380-
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
381-
handle.close()
382388

383389

384390
def release_nonce_reservation_atomic(nonce: str) -> None:
385391
"""Drop an in-flight reservation when remote mutation did not succeed."""
386-
handle, state = _lock_nonce_state()
387-
try:
392+
with _locked_nonce_state() as (handle, state):
388393
_prune_nonce_state(state)
389394
entry = state.get("reservations", {}).get(nonce)
390395
if entry and not entry.get("remote_applied"):
391396
del state["reservations"][nonce]
392397
_write_nonce_state(handle, state)
393-
finally:
394-
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
395-
handle.close()
396398

397399

398400
def nonce_is_consumed(nonce: str) -> bool:
399-
handle, state = _lock_nonce_state()
400-
try:
401+
with _locked_nonce_state() as (_handle, state):
401402
_prune_nonce_state(state)
402403
return nonce in state["nonces"]
403-
finally:
404-
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
405-
handle.close()
406404

407405

408406
def consume_nonce_atomic(nonce: str, require_remote_applied: bool = True) -> bool:
409407
"""Mark nonce consumed once after remote success. Returns False if invalid."""
410-
handle, state = _lock_nonce_state()
411-
try:
408+
with _locked_nonce_state() as (handle, state):
412409
_prune_nonce_state(state)
413410
if nonce in state["nonces"]:
414411
return False
@@ -421,9 +418,6 @@ def consume_nonce_atomic(nonce: str, require_remote_applied: bool = True) -> boo
421418
del state["reservations"][nonce]
422419
_write_nonce_state(handle, state)
423420
return True
424-
finally:
425-
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
426-
handle.close()
427421

428422

429423
def verify_grant_fields(
@@ -434,8 +428,13 @@ def verify_grant_fields(
434428
action: str = DEFAULT_ACTION,
435429
check_nonce_consumed: bool = True,
436430
) -> tuple[bool, str]:
431+
try:
432+
_validate_repo_slug(repo)
433+
except GrantError as exc:
434+
return False, str(exc)
435+
437436
if fields.get("marker") != GRANT_MARKER:
438-
if "operator-grant-v1" in str(fields):
437+
if fields.get("marker") == "operator-grant-v1":
439438
return False, (
440439
"operator-grant-v1 is no longer accepted; re-run grant with matching "
441440
"--file or --message in an operator terminal"
@@ -602,13 +601,9 @@ def release_grant_for_append(
602601
if not nonce:
603602
return False, "grant missing grant-nonce"
604603
entry = None
605-
handle, state = _lock_nonce_state()
606-
try:
604+
with _locked_nonce_state() as (_handle, state):
607605
_prune_nonce_state(state)
608606
entry = state.get("reservations", {}).get(nonce)
609-
finally:
610-
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
611-
handle.close()
612607
if entry and not _reservation_matches(entry, repo, str(pr_number), digest):
613608
return False, "grant nonce reserved for a different append operation"
614609
release_nonce_reservation_atomic(nonce)
@@ -685,6 +680,7 @@ def mint_grant(
685680
message: str | None,
686681
cwd: Path | None = None,
687682
) -> Path:
683+
_validate_repo_slug(repo)
688684
digest = content_digest_for_append(file_path, message, cwd=cwd)
689685
secret = resolve_hmac_secret(allow_generate=True)
690686
issued_at = _now_utc().isoformat().replace("+00:00", "Z")
@@ -710,9 +706,7 @@ def mint_grant(
710706
f"grant-nonce={nonce}\n"
711707
f"token={token}\n"
712708
)
713-
fd = os.open(str(ACK_PATH), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
714-
with os.fdopen(fd, "w", encoding="utf-8") as handle:
715-
handle.write(body)
709+
_write_private_file(ACK_PATH, body)
716710
return ACK_PATH
717711

718712

@@ -908,8 +902,11 @@ def add_append_args(p: argparse.ArgumentParser) -> None:
908902
reconcile_p.set_defaults(func=_cmd_reconcile)
909903

910904
args = parser.parse_args(argv)
911-
if args.command != "reconcile" and not args.file and not args.message:
912-
parser.error("provide --file or --message")
905+
if args.command != "reconcile":
906+
if not args.file and not args.message:
907+
parser.error("provide --file or --message")
908+
if args.file and args.message:
909+
parser.error("provide --file or --message, not both")
913910
return args.func(args)
914911

915912

scripts/git/guard-sync-manifest.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ GUARD_SYNC_EXECUTABLES=(
3737
remind-pr-body-append-only.sh
3838
publish-clean-branch.sh
3939
verify-pr-body-not-clobbered.sh
40+
scrub_dsstore.sh
4041
)
4142

4243
# Non-executable policy/data files (mode 0644 when synced).

0 commit comments

Comments
 (0)