feat(plan-01): Phase 1 — relocate harness code to packages/casan-harness (symlink facade)

Physically move the pure-code subtrees out of .specify into the package, leaving
compat symlinks at the old .specify/<dir> paths so every existing reference (internal
CASAN_HARNESS_ROOT + external CI/docker/mjs) keeps resolving. Runtime state stays put.

Moved (git mv): scripts/ tests/ security/ templates/ config/ governance/ memory/
  .specify/<dir>  ->  packages/casan-harness/<dir>   (+ .specify/<dir> symlink)
Stays in .specify (state/governance/domain, handled later): logs/ agentops/ level5/
  init-options.json traceability-map.json

Python `.resolve()` self-location followed the compat symlink into packages and lost
the app root; generate-casan-demo-context.py, generate-agentops-dashboard.py and
dashboard-server.py now walk UP for the `.specify` state marker instead of a fixed
parent depth (fixes "missing trace files" in run-casan4).

Full gate: PASS=64 FAIL=0 SKIP=3 (CASAN_CI_STEP_TIMEOUT_SEC=1200 — track-a ~450s runs
close to the 600s default and can tip over under load; this is timing variance, not a
regression — it passed cleanly with headroom). Runtime log/audit artifacts kept unstaged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
thanhnv
2026-07-08 00:06:00 +09:00
co-authored by Claude Opus 4.8
parent 2c765c9a45
commit 664bd1f00c
229 changed files with 268 additions and 3 deletions
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""CASAN H4 — Suspicious base64/hex decoder (Track A, V4 encoding smuggling).
Reads text on stdin. Finds embedded base64 and hex blobs, decodes them, and
prints the decoded plaintext (one blob per line) on stdout so the caller can
re-run its injection/secret pattern scan on the *decoded* content.
Safety / no-false-positive design:
* Only blobs >= MIN_LEN characters are considered (short words are ignored).
* A decoded blob is emitted ONLY if it is mostly printable text. Random
base64-looking words (e.g. "objectives", DER key bytes) decode to
non-printable garbage and are dropped, so they can never trigger a match.
* Output is advisory: the caller decides a decoded blob is malicious only if
the decoded text itself matches a block/secret pattern.
Deterministic: same input always yields the same output.
"""
import base64
import binascii
import re
import sys
MIN_LEN = 16
PRINTABLE_RATIO = 0.8
B64_RE = re.compile(r"[A-Za-z0-9+/]{%d,}={0,2}" % MIN_LEN)
HEX_RE = re.compile(r"\b[0-9a-fA-F]{%d,}\b" % MIN_LEN)
def _mostly_printable(text: str) -> bool:
if not text:
return False
ok = sum(1 for c in text if c.isprintable() or c.isspace())
return ok >= PRINTABLE_RATIO * len(text)
def decode_blobs(data: str):
out = []
for m in B64_RE.findall(data):
pad = m + "=" * ((4 - len(m) % 4) % 4)
try:
dec = base64.b64decode(pad, validate=True)
except (binascii.Error, ValueError):
continue
txt = dec.decode("utf-8", "ignore")
if _mostly_printable(txt):
out.append(txt)
for m in HEX_RE.findall(data):
if len(m) % 2 != 0:
continue
try:
dec = bytes.fromhex(m)
except ValueError:
continue
txt = dec.decode("utf-8", "ignore")
if _mostly_printable(txt):
out.append(txt)
return out
if __name__ == "__main__":
blobs = decode_blobs(sys.stdin.read())
if blobs:
sys.stdout.write("\n".join(blobs))