stonefish-labs's picture
d3-mini-agentcode v0.1-preview (pre-release)
21a9138 verified
Raw History Blame Contribute Delete
10.7 kB
"""Language-neutral patterns: credential locations, persistence locations, TLS/registry settings, obfuscation markers
and the workspace-scope classifier for paths.
"""
from __future__ import annotations
import re
_SEP = r"""['"\s,/\\+)(]{0,8}""" # tolerate Path("~") / ".ssh" / "id_rsa" style construction
SECRET_PATTERNS = [
("~/.ssh", re.compile(r"\.ssh\b(?!_)" + r"|\bid_(rsa|dsa|ecdsa|ed25519)\b|\bauthorized_keys\b|\bknown_hosts\b")),
("~/.aws credentials", re.compile(r"\.aws" + _SEP + r"(credentials|config)\b|\bAWS_SECRET_ACCESS_KEY\b|\bAWS_SESSION_TOKEN\b")),
("~/.npmrc", re.compile(r"\.npmrc\b|_authToken\b")),
("~/.pypirc", re.compile(r"\.pypirc\b")),
("~/.netrc", re.compile(r"(?<![\w])\.netrc\b|\b_netrc\b")),
("git credentials", re.compile(r"\.git-credentials\b|credential\.helper\b")),
("docker config", re.compile(r"\.docker" + _SEP + r"config\.json")),
("kube config", re.compile(r"\.kube" + _SEP + r"config\b|\bKUBECONFIG\b")),
("gcloud credentials", re.compile(r"(\.config" + _SEP + r")?gcloud" + _SEP + r"\w*credentials|application_default_credentials|GOOGLE_APPLICATION_CREDENTIALS")),
("azure credentials", re.compile(r"\.azure" + _SEP + r"(accessTokens|msal_token_cache|azureProfile)")),
("gnupg", re.compile(r"\.gnupg\b|secring\.gpg|private-keys-v1\.d")),
(".env file", re.compile(r"""(^|['"/\s])\.env(\.(local|production|prod|development|dev|test|staging))?(['"\s]|$)""")),
("keychain", re.compile(r"\bkeychain\b|login\.keychain|find-generic-password|find-internet-password|\bkeyring\.get_password|secret-tool\s+lookup|libsecret|CredRead\w*|vaultcmd", re.I)),
("browser credential store", re.compile(r"Login Data\b|\bCookies\.sqlite|\bkey4\.db\b|logins\.json|Local State\b|\bcookies\.sqlite\b", re.I)),
("crypto wallet", re.compile(r"wallet\.dat\b|\bkeystore\b.*\bethereum|\.electrum\b|\bexodus\b.*wallet|metamask", re.I)),
("/etc/shadow", re.compile(r"/etc/(shadow|sudoers|gshadow)\b")),
("cloud metadata", re.compile(r"169\.254\.169\.254|metadata\.google\.internal|/latest/meta-data|100\.100\.100\.200")),
("ssh/gh/cloud CLI token files", re.compile(r"\.config" + _SEP + r"(gh" + _SEP + r"hosts\.yml|hub\b)|\.terraform\.d" + _SEP + r"credentials|\.vault-token|\.cargo" + _SEP + r"credentials|\.gem" + _SEP + r"credentials|\.composer" + _SEP + r"auth\.json")),
]
TOKEN_LITERAL = re.compile(r"\b(ghp_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{40,}|gh[ousr]_[A-Za-z0-9]{30,}|xox[abpr]-[A-Za-z0-9-]{10,}|"
r"AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{36}|pypi-[A-Za-z0-9_-]{50,}|"
r"-----BEGIN (RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----)")
CI_SECRET = re.compile(r"\$\{\{\s*secrets\.([A-Za-z0-9_]+)\s*\}\}")
SECRET_ENV_NAME = re.compile(r"(TOKEN|SECRET|PASSW(OR)?D|PASSWD|API_?KEY|PRIVATE_KEY|ACCESS_KEY|CREDENTIAL|AUTH)", re.I)
SECRET_MARKER_LITERAL = re.compile(r"""['"](TOKEN|SECRET|KEY|PASSWORD|PASSWD|CREDENTIAL|AUTH|API_KEY|PRIVATE)['"]""", re.I)
def secret_hits(text: str):
hits = [label for label, rx in SECRET_PATTERNS if rx.search(text)]
m = TOKEN_LITERAL.search(text)
if m:
hits.append("token literal " + m.group(0)[:12] + "…")
return hits
PERSIST_LOCATIONS = [
("shell rc file", re.compile(r"(?<![\w-])\.(bashrc|bash_profile|bash_login|zshrc|zprofile|zshenv|zlogin|profile|"
r"cshrc|tcshrc|kshrc|config/fish/config\.fish)\b|/etc/(profile|bash\.bashrc|zshrc|profile\.d)\b"
r"|\bPROMPT_COMMAND\b")),
("cron", re.compile(r"/etc/cron|/var/spool/cron|\bcrontab\b|@reboot\b|\bcron\.d\b")),
("git hooks", re.compile(r"\.git" + _SEP + r"hooks\b|\bcore\.hooksPath\b|\.husky/|"
r"""['"/](pre-commit|post-commit|post-checkout|post-merge|pre-push|prepare-commit-msg|commit-msg|post-rewrite)['"]""")),
("systemd unit", re.compile(r"/etc/systemd/|\.config" + _SEP + r"systemd|systemd" + _SEP + r"user|\.service['\"]|\bsystemctl\s+(--user\s+)?enable\b")),
("launchd", re.compile(r"LaunchAgents|LaunchDaemons|\blaunchctl\s+(load|bootstrap)")),
("autostart", re.compile(r"\.config" + _SEP + r"autostart|\\Start Menu\\Programs\\Startup|CurrentVersion\\Run\b|"
r"\bschtasks\b|/etc/rc\.local|/etc/init\.d/|\bupdate-rc\.d\b|\.xinitrc|\.xprofile")),
("python startup (sitecustomize/.pth)", re.compile(r"\b(sitecustomize|usercustomize)(\.py)?\b|['\"][^'\"]*\.pth['\"]|site-packages" + _SEP + r"[\w-]*\.pth")),
("editor/IDE autorun", re.compile(r"\.vscode" + _SEP + r"tasks\.json|runOn['\"]?\s*:\s*['\"]folderOpen")),
("ssh authorized_keys", re.compile(r"authorized_keys")),
]
CRON_LINE = re.compile(r"^\s*(@(reboot|hourly|daily|weekly|monthly|yearly|annually)|"
r"([\d*/,-]+\s+){4}[\d*/,A-Za-z-]+)\s+\S")
TLS_OFF = [
(re.compile(r"\bverify\s*=\s*False\b"), "verify=False"),
(re.compile(r"ssl\._create_unverified_context|_create_default_https_context\s*="), "ssl unverified context"),
(re.compile(r"\bCERT_NONE\b"), "ssl.CERT_NONE"),
(re.compile(r"check_hostname\s*=\s*False"), "check_hostname=False"),
(re.compile(r"urllib3\.disable_warnings|InsecureRequestWarning"), "urllib3 insecure warnings silenced"),
(re.compile(r"rejectUnauthorized['\"]?\s*:\s*false|rejectUnauthorized\s*=\s*false"), "rejectUnauthorized: false"),
(re.compile(r"NODE_TLS_REJECT_UNAUTHORIZED['\"]?\]?\s*[:=]\s*['\"]?0"), "NODE_TLS_REJECT_UNAUTHORIZED=0"),
(re.compile(r"PYTHONHTTPSVERIFY['\"]?\]?\s*[:=]\s*['\"]?0"), "PYTHONHTTPSVERIFY=0"),
(re.compile(r"strict-ssl\s*[=:]?\s*['\"]?false|strictSSL['\"]?\s*:\s*false|enableStrictSsl['\"]?\s*:\s*false", re.I), "strict-ssl false"),
(re.compile(r"InsecureSkipVerify\s*:\s*true|insecure_skip_verify\s*=\s*true", re.I), "InsecureSkipVerify"),
(re.compile(r"sslverify\s*=?\s*false|GIT_SSL_NO_VERIFY", re.I), "git sslVerify off"),
(re.compile(r"\bssl\s*=\s*False\b|\bverify_ssl\s*=\s*False\b|\bverify_certs\s*=\s*False\b"), "ssl=False"),
]
REGISTRY = [
(re.compile(r"^\s*(@[\w-]+:)?registry\s*=\s*(\S+)", re.M), "npm registry"),
(re.compile(r"""['"]registry['"]\s*:\s*['"]([^'"]+)['"]"""), "registry setting"),
(re.compile(r"npmRegistryServer\s*:\s*['\"]?(\S+?)['\"]?\s*$", re.M), "yarn registry"),
(re.compile(r"^\s*(--)?(index-url|extra-index-url|trusted-host|find-links)\s*[= ]\s*(\S+)", re.M), "pip index"),
(re.compile(r"""\b(index[-_]url|extra[-_]index[-_]url|trusted[-_]host)['"]?\s*[:=,]\s*\[?\s*['"]?([^'"\s,\]]+)""", re.I), "python index"),
]
PRIV_GRANT = [
(re.compile(r"\bis_superuser\s*[=:]\s*True|['\"]is_superuser['\"]\s*:\s*True"), "account superuser grant"),
(re.compile(r"\bis_staff\s*[=:]\s*True|['\"]is_staff['\"]\s*:\s*True"), "account staff grant"),
(re.compile(r"\bcreatesuperuser\b|create_superuser\("), "superuser creation"),
(re.compile(r"ALLOWED_HOSTS\s*=\s*\[\s*['\"]\*['\"]\s*\]"), "ALLOWED_HOSTS=*"),
(re.compile(r"CORS_(ORIGIN_)?ALLOW_ALL(_ORIGINS)?\s*=\s*True"), "CORS allow all"),
(re.compile(r"\bpermissions\s*:\s*write-all\b"), "CI token permissions write-all"),
(re.compile(r"\bAllowAny\b|permission_classes\s*=\s*\[\s*\]|@csrf_exempt|csrf\s*=\s*False"), "auth/CSRF check relaxed"),
(re.compile(r"NOPASSWD\s*:"), "sudo NOPASSWD"),
(re.compile(r"\bchmod\b.*\b(777|u\+s|4755)\b|0o?777\b|S_ISUID"), "broad/setuid permission"),
]
OBF_HEX_NAME = re.compile(r"\b_0x[0-9a-fA-F]{3,}\b")
OBF_PACKED = re.compile(r"eval\(function\(p,a,c,k,e,[rd]\)|\bJSFuck\b|\[\]\[\(!\[\]\+\[\]\)|\bjjencode\b|゚ω゚")
OBF_B64 = re.compile(r"[A-Za-z0-9+/]{200,}={0,2}")
OBF_HEX = re.compile(r"(?:\\x[0-9a-fA-F]{2}){20,}|\b[0-9a-fA-F]{240,}\b")
OBF_CHARCODES = re.compile(r"fromCharCode\s*\(\s*(\d+\s*,\s*){8,}|\bchr\(\d+\)\s*\+\s*chr\(\d+\)\s*\+\s*chr\(|(\[\s*(\d{2,3}\s*,\s*){24,}\d{2,3}\s*\])")
BIDI = re.compile("[‪-‮⁦-⁩​‌‍⁠]")
def obfuscation(line: str, lang: str):
out = []
if lang in ("javascript", "typescript", "tsx", "sfc", "python", "shell", "other"):
n = len(OBF_HEX_NAME.findall(line))
if n >= 2:
out.append((f"_0x identifiers ({n} on line)", "hex-named identifiers"))
if OBF_PACKED.search(line):
out.append(("packed/encoded JS", "packer signature"))
m = OBF_CHARCODES.search(line)
if m:
out.append(("charcode array/sequence", "numeric character codes"))
m = OBF_B64.search(line)
if m and not re.fullmatch(r"[A-Za-z]+", m.group(0)[:200]) and not re.search(r"sha\d+-|integrity|\"resolved\"", line):
out.append((f"base64-like blob ({len(m.group(0))} chars)", "long encoded literal"))
m = OBF_HEX.search(line)
if m and not re.search(r"sha\d+|integrity|hash|digest", line, re.I):
out.append((f"hex blob ({len(m.group(0))} chars)", "long encoded literal"))
if BIDI.search(line):
out.append(("bidi/zero-width unicode", "invisible or direction-changing characters"))
if len(line) > 2000 and lang in ("javascript", "typescript", "python", "tsx", "sfc"):
out.append((f"very long line ({len(line)} chars)", "minified or packed"))
return out
def path_scope(expr: str) -> str:
"""Classify a path expression: home, system, temp, workspace or unknown. Purely textual."""
e = expr.strip().strip("'\"`")
if not e or e == "dynamic":
return "unknown"
m = re.match(r"^\(?(?:pathlib\.)?(?:Path|PurePath|os\.path\.join|path\.join|path\.resolve)\(\s*['\"]([^'\"]+)['\"]", e)
if m and not re.search(r"home|expanduser|tmp|temp|environ|getenv|process\.env", e, re.I):
e = m.group(1)
if re.search(r"(^~|\$HOME\b|\$\{HOME\}|%USERPROFILE%|%APPDATA%|\bhomedir\(\)|Path\.home\(\)|expanduser|"
r"\bos\.homedir|HOMEPATH|\bAPPDATA\b|LOCALAPPDATA|getenv\(['\"]HOME|environ\[['\"]HOME|process\.env\.HOME)", e):
return "home"
if re.search(r"(^/tmp\b|^/var/tmp\b|\btempfile\b|\bmkdtemp|\bmkstemp|\btmpdir\(\)|\bgettempdir|\$TMPDIR|\bTemporaryDirectory|\btmp_path\b|\btmpdir\b|RUNNER_TEMP)", e):
return "temp"
if re.match(r"^(/(etc|usr|bin|sbin|lib|lib64|opt|var|boot|root|System|Library|Applications|private)\b|[A-Za-z]:\\\\?(Windows|Program))", e):
return "system"
if re.match(r"^/dev/", e):
return "device"
if re.search(r"(^|/)\.\.(/|$)", e) and not re.search(r"__file__|__dirname|import\.meta", e):
return "parent of workspace?"
if re.match(r"^/", e):
return "absolute (other)"
if re.match(r"^[\w.@-][\w./@-]*$", e) or re.search(r"__file__|__dirname|import\.meta\.url|cwd\(\)|getcwd", e):
return "workspace"
return "unknown"