Files
CASAN/packages/casan-harness/scripts/bash/decode-suspicious.py
T
thanhnvandClaude Opus 4.8 36a4812ef3 refactor(structure): promote app to repo root + remove redundant workspace cruft
Standard production layout: the OKR app (was nested under AINative_OKR_CASAN5/) is now
the repository root. No more wrapper directory.

- Promote AINative_OKR_CASAN5/* -> repo root (backend/ frontend/ packages/ apps/
  .specify/ docs/ infra/ nginx/ scripts/ + configs). Merge tool dirs: .gitea (kept the
  active deploy ci.yml, added harness-ci.yml + runbooks), .claude (agents/commands +
  launch.json), .github moved up.
- Remove redundant: 00_SUBMISSION_PACKAGE, scattered root notes (FPT_CASAN_Full.md,
  tu-tuong-casan.md, casan-tu-sinh..., casan_harness_assessment.md, source-review...,
  README_CASAN5_REFINED.md), casan-next-plans/ and optimize-docs/ (competition/planning
  artifacts — roadmap + design history preserved in git log / commit messages).
- Update all references to the old layout:
  - .gitea/workflows/{ci,harness-ci}.yml, .github/workflows/{ci,deploy}.yml:
    working-directory .; drop AINative_OKR_CASAN5/ prefix; .specify/{tests,scripts}
    -> packages/casan-harness/... (.specify/logs state kept)
  - .claude/launch.json, .gitea/*-runbook.md: path prefixes
  - CLAUDE.md, README.md: docs/input -> apps/okr/domain/input
  - policy-bundle.yaml: 8 policy paths -> packages/casan-harness/...; manifest re-signed
- secrets-scan.sh: fixture excludes -> new package/domain paths.

Full gate from the new root: PASS=64 FAIL=0 SKIP=3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 13:26:36 +09:00

65 lines
2.0 KiB
Python
Executable File

#!/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))