
    o#jc                    P   U d Z ddlZddlZddlZddlZddlmZmZ ddlm	Z	  e	e
      j                         j                  d   Ze G d d             Ze G d d	             Ze G d
 d             Zg  edddddddddg d
       edddddddg d       edd d!d"d#d$d%dd&'	       ed(d)d*dd#d+d,dd-g d.
       ed/d0d1d"d#d2d3d4d5g       ed6d7d8d"d#d9d:d;d<g d=
       ed>d?d@ddAdBdCddDg dE
       edFdGdHd"dAdIdJdKdLg dM
       edNdOdPd"dAdQdRddSg dE
       edTdUdVd"d#dWdXdKdYg dZ
       ed[d\d]d"d#d^d_dd`g da
       edbdcddd"d#dedfddgd4dhg
       edidjdkd"dldmdnddog dM
       edpdqdrddsdtdudKdvg dM
       edwdxdydddzd{dd|g d}
       ed~ddd"dddddg d}
       edddddddddg d
       edddd"dAddddg dM
       edddddddddg d
       eddddd#ddddg d
       edddddAddddg d
       edddddAdddKdg dM
       eddddd#ddddg d
       eddddd#ddddd4dhg
       eddddd#ddddg d
       edddd"dddddg dǢ
       edddd"ddddKdg d΢
       eddddd#dddKdg d
       edddddddddg dۢ
       edddddAddddg dE
       edddddAddddg dM
       eddddd#ddddg d
       edddddAddddg dM
       eddddd#ddddg d
       edddddAddddg dM
       edddd"d#d dddg d
       edddd"dAddddg dM
       edd	d
d"d#dddKdg d
       edddd"dAdddKdg d
       edddd"ddddddg
       eddd dd#d!d"dd#g d
       ed$d%d ddAd&d"dd#g dM
       ed'd(d)d"d#d*d+dd,g d
       ed-d.d/d"d#d0d1dd2g d3
       ed4d5d6dd7d8d9dd:'	       ed;d<d=dd7d>d?d;d@'	       edAdBdCd"d7dDdEddF'	       edGdHdIdddJdKddLddMg
       edNdOdPd"dQdRdSddT'	       edUdVdWd"dQdXdYddZ'	       ed[d\d]dd^d_d`ddaddMg
       edbdcdddd^dedfddgddMg
       edhdidjdd^dkdlddmddMg
       edndodpdd#dqdrdKdsg d
       edtdudvddAdwdxdKdyg dM
       edzd{d|dd#d}d~ddg d
       edddddAddddg dM
       eddddddddd'	       eddddddddd'	       eddddddddd'	       eddddddddd'	       eddddd#dddKdg d
       edddddAdddKdg dM
       edddd"ddddKdg d
       edddd"dddddddg
       edddd"ddddd'	       eddddd#dĐdddƐdǐȫ
       edɐdʐdd"dAd̐dddά'	       edϐdАdd"dҐdӐddKdՐd֐ȫ
       edאdؐdd"dldڐdddܐdݐȫ
       edސdߐddd#dddKddȫ
       edddddҐdddKd'	       edddddddddg d
      Z edddgdgdggd eddddd#dd      g       ed dddgddggd edddd"d#d	d
dKd'	       eddddd#dddd'	      g       edddgddggd eddddd#dddd'	      g       edddgg d g d!gd" ed#d$d%d"d&d'd(dd)'	       ed*d+d,ddd-d(dd.'	      g       ed/d0d1d2gd1d3gd4gdd2ggd5 ed6d7d8d"dAd9d:dd;'	       ed<d=d>d"dAd?d@ddA'	      g       edBdCdDgg dEgdF edGdHdIddldJdKddL'	      g       edMdNdOgg dPgdF edQdRdSd"dsdTdUddV'	      g       edWdXdYgdZggd5 ed[d\d]d"ddddKd^'	      g       ed_d`g dadbggd5 edcddded"dAdIdJdKdL'	       edfdgdhd"dAddddi'	      g       edjdkdlgdmdngg dodpgg dqdrggds edtdudvddҐdwdx       edydzd{d"dQd|d}dKd~'	      g       eddg dddgddgddgddggd eddddd#dd      g       edddgdgddggd edddd"ddddd'	       eddddd#dddd'	      g      gZee   ed<   d Zd Zd Zd ZddZddddZddddZdZd Zd Zd Zd Z dee   fdZ!da"edz  ed<   defdZ#defdZ$de%dee&e%e'f      fdZ(de%dede)fdZ*de%de	dee   fdZ+de	de	de,e%ee   f   fdZ-d Z.e/dk(  r ej`                   e.              yy(  um  
Error Pattern Detector — Dynamic Bug Pattern Scanner

Scans the codebase for patterns that have caused bugs before.
When a new bug is fixed, add a pattern rule to the PATTERNS list
so the scanner catches similar issues elsewhere.

Stack-agnostic scanner engine with universal starter patterns.
Add project-specific patterns to the PATTERNS list as bugs are discovered.

Usage:
    detect_error_patterns.py                    # Full scan
    detect_error_patterns.py --staged           # Staged files only
    detect_error_patterns.py --severity high    # Filter by severity
    detect_error_patterns.py --json             # JSON output (for CI)
    detect_error_patterns.py --summary          # One-line summary
    detect_error_patterns.py --list-rules       # List all pattern rules
    detect_error_patterns.py --analyze "error"  # Reactive error analysis
    detect_error_patterns.py --analyze-log f.log # Analyze log file for errors
    detect_error_patterns.py --list-knowledge   # List knowledge base entries

Exit codes:
    0 — No high-severity findings
    1 — High-severity findings detected
    2 — Script error
    N)	dataclassfield)Path   c                       e Zd ZU dZeed<   eed<   eed<   eed<   eed<   eed<   dZeed	<   d
Zeed<   dZ	eed<   dZ
eed<    ee      Zeed<    ee      Zeed<   y)PatternRulez!A single error pattern to detect.idnamedescriptionseverity	file_globpattern negative_patternr   context_linesfix_hintincidentdefault_factoryexamplesexclude_pathsN)__name__
__module____qualname____doc__str__annotations__r   r   intr   r   r   listr   r        </home/rui/Apps/pvtcoms/scripts/docs/detect_error_patterns.pyr   r   *   sk    +G
IMNLcM3HcHc40Hd05M45r!   r   c                   b    e Zd ZU dZeed<   eed<   eed<   eed<   eed<   eed<   eed<   eed	<   y
)FindingzA detected pattern match.rule_id	rule_namer   filelinecoder   r   N)r   r   r   r   r   r   r   r    r!   r"   r$   r$   ;   s-    #LNM
I
I
IMMr!   r$   c                   ~    e Zd ZU dZeed<   eed<   ee   ed<   eee      ed<   eed<    ee      Zee	   ed<   d	 Z
y
)ErrorKnowledgez?Maps error fingerprints to code patterns for reactive analysis.r	   r
   keywordsalt_keywordscategoryr   rulesc                 ,    | j                   sg | _         y y N)r-   )selfs    r"   __post_init__zErrorKnowledge.__post_init__R   s       "D !r!   N)r   r   r   r   r   r   r   r   r/   r   r3   r    r!   r"   r+   r+   H   sE    IG
I3itCy/!M$T:E4:#r!   r+   zEP-001zPotential hardcoded secretz8Hardcoded passwords, API keys, or tokens in source code.highz**/*zY(?:password|passwd|pwd|secret|api_key|apikey|token|auth_token)\s*[:=]\s*["'][^"']{8,}["']zIexample|placeholder|test|mock|fake|dummy|changeme|your[_-]|CHANGE_ME|TODO   zPUse environment variables or a secrets manager instead of hardcoding credentials)test__test.z.test.zspec/mockfixturenode_modules.git)
r	   r
   r   r   r   r   r   r   r   r   zEP-002zTODO/FIXME/HACK marker in codez8Code markers that indicate incomplete or temporary work.lowz8(?:#|//|/\*)\s*(?:TODO|FIXME|HACK|XXX|TEMP|WORKAROUND)\bz5Resolve the TODO or create a backlog item to track it)r:   r;   __pycache__vendor/)r	   r
   r   r   r   r   r   r   zEP-010zBare except clausezQBare 'except:' catches all exceptions including KeyboardInterrupt and SystemExit.mediumz**/*.pyz^\s*except\s*:znoqa|type:\s*ignorez4Use 'except Exception:' or a specific exception type)	r	   r
   r   r   r   r   r   r   r   zEP-011zeval() or exec() usagezHDynamic code execution is a security risk and makes debugging difficult.z\b(?:eval|exec)\s*\(znoqa|# safe|# trustedzTUse ast.literal_eval() for data parsing, or refactor to avoid dynamic code execution)r6   setup.pyconftest.pyzEP-012zMutable default argumentzVUsing mutable objects (list, dict, set) as default arguments causes shared state bugs.zRdef\s+\w+\s*\([^)]*:\s*(?:list|dict|set|List|Dict|Set)\s*=\s*(?:\[\]|\{\}|set\(\))zJUse None as default and create the mutable object inside the function bodyr6   r=   zEP-013zSync I/O in async functionzICalling sync file/network I/O in an async function blocks the event loop.zM(?:open\s*\(|\.read_text\s*\(|\.read_bytes\s*\(|\.write_text\s*\(|requests\.)z,run_in_executor|aiofiles|anyio|aiohttp|httpx
   z[Use aiofiles or run_in_executor for file I/O, use httpx/aiohttp for HTTP in async functions)r6   rA   zmigrations/zalembic/zEP-020zconsole.log in production codezLConsole statements left in production code pollute output and may leak data.z**/*.{ts,tsx,js,jsx}z%\bconsole\.(log|debug|info|warn)\s*\(zeslint-disable|logger|// keepzKUse a proper logging library or remove console statements before committing)testspec	__tests__r:   .nextdist/zEP-021z%Unhandled promise (no catch or await)zMCalling an async function without await or .catch() silently swallows errors.zH(?:fetch|axios\.\w+|\.post|\.get|\.put|\.delete|\.patch)\s*\([^)]*\)\s*;zawait|\.then|\.catch|try\s*\{   z6Add 'await' or '.catch()' to handle promise rejections)rC   rD   rE   r:   zEP-070z"Loose equality (== instead of ===)zO== performs type coercion which causes subtle bugs (e.g., '' == false is true).z(?<![!=<>])=[=](?!=)\sz2eslint-disable|# intentional|null\s*==\s|==\s*nullzQUse === for strict equality. Exception: == null to check both null and undefined.zEP-071z!File open without context managerzNUsing open() without 'with' risks leaving file handles unclosed on exceptions.z^\s*\w+\s*=\s*open\s*\(z/# managed|# closed below|contextmanager|closingz?Use 'with open(...) as f:' to ensure the file is always closed.)r6   r7   rA   r@   zEP-072zWildcard importzY'from x import *' pollutes the namespace and makes it impossible to trace symbol origins.z^\s*from\s+\S+\s+import\s+\*z# noqa|__init__|# re-exportz:Import specific names: 'from module import ClassA, func_b')r6   r7   z__init__.pyrA   zEP-073zIdentity check on non-singletonzSUsing 'is' to compare strings, numbers, or lists checks object identity, not value.z\bis\s+(?:["']\w|[0-9]|\[|\()z<is\s+None|is\s+True|is\s+False|is\s+not\s+None|# intentionalzXUse == for value comparison. 'is' is only correct for None, True, False, and singletons.r7   zEP-074zReact list without key propzPRendering lists without unique key props causes unnecessary re-renders and bugs.z**/*.{tsx,jsx}z8\.map\s*\([^)]*\)\s*(?:=>|\.)\s*(?:<\w+)(?![^>]*key\s*=)zkey=|# no key neededzRAdd a unique key prop to each element rendered in a .map(): <Item key={item.id} />zEP-075zDirect state mutation in ReactzUMutating state directly (push, splice, assignment) bypasses React's change detection.z**/*.{tsx,jsx,ts,js}zW(?:state\.\w+\s*=\s|state\.\w+\.push\s*\(|state\.\w+\.splice\s*\(|state\.\w+\.pop\s*\()zKuseState|useReducer|this\.setState|# immutable copy|\.slice\(\)|\.\.\.statezWUse setState/useState setter with a new object/array: setState(prev => [...prev, item])zEP-076zSELECT * in production queryzUSELECT * fetches unnecessary columns, wastes bandwidth, and breaks on schema changes.z((?:SELECT|select)\s+\*\s+(?:FROM|from)\sz<# ok|COUNT\(\*\)|count\(\*\)|test|migration|-- select|EXISTSz8List specific columns: SELECT id, name, email FROM users)r6   r7   	migrationalembicr:   zEP-077zUnbounded query (no LIMIT)zLQueries without LIMIT can return millions of rows and crash the application.zq(?:SELECT|select)\s+(?!\*\s*FROM.*(?:LIMIT|limit|TOP|top|FETCH|fetch)).*(?:FROM|from)\s+\w+\s*(?:WHERE|where|;|$)zhLIMIT|limit|TOP|top|FETCH|fetch|COUNT|count|EXISTS|exists|migration|\.first\(|\.one\(|\.get\(|\.scalar\(zSAdd LIMIT clause to prevent unbounded result sets. ORM: use .limit() or pagination.zEP-078z'Hardcoded host/port in application codezRHardcoded localhost or port numbers in non-config files break across environments.z**/*.{py,ts,tsx,js,jsx}z*["'](?:localhost|127\.0\.0\.1):\d{4,5}["']zV# config|# default|os\.environ|getenv|process\.env|\.env|test|fixture|example|fallbackz?Use environment variables or config files for host/port values.)r6   r7   rA   r:   z.envconfigzEP-079z%Async function without error handlingzRAsync arrow functions in event handlers without try/catch silently swallow errors.z/(?:on\w+|addEventListener)\s*(?:=|\()\s*async\sz(try\s*\{|\.catch|# handled|ErrorBoundary   z@Wrap async event handlers in try/catch or use an error boundary.zEP-080zRegex denial of service riskzXNested quantifiers like (a+)+ or (a|a)* cause exponential backtracking on crafted input.z[(?:re\.compile|new\s+RegExp|/)\s*\(?["'/].*(?:\([^)]*[+*]\)\s*[+*]|\([^)]*\|[^)]*\)\s*[+*])z4# safe|# bounded|# validated input|atomic|possessivez[Avoid nested quantifiers. Use atomic groups or rewrite the pattern to prevent backtracking.)r6   r7   r:   zEP-030zSQL string concatenationzRBuilding SQL queries with string concatenation or f-strings enables SQL injection.z[(?:execute|cursor\.execute|\.raw|\.extra)\s*\(\s*(?:f["']|["'].*%s|["'].*\+|["'].*\.format)z$# safe|# parameterized|noqa|sanitizezUUse parameterized queries: cursor.execute('SELECT * FROM t WHERE id = %s', [user_id]))r6   r7   rI   rJ   zEP-031z SQL string concatenation (JS/TS)zSBuilding SQL queries with template literals or concatenation enables SQL injection.z:(?:\.query|\.execute|\.raw)\s*\(\s*(?:`[^`]*\$\{|['"].*\+)z+parameterized|prepared|placeholder|sanitizezNUse parameterized queries: db.query('SELECT * FROM t WHERE id = $1', [userId]))rC   rD   rE   r:   rI   zEP-032zDangerous HTML injectionzTUsing dangerouslySetInnerHTML or innerHTML without sanitization enables XSS attacks.z+(?:dangerouslySetInnerHTML|\.innerHTML\s*=)z+sanitize|DOMPurify|purify|escape|# safe|xsszGSanitize HTML with DOMPurify before injecting: DOMPurify.sanitize(html)zEP-033zShell injection riskzFUsing shell=True with user-controlled input enables command injection.zPsubprocess\.(?:call|run|Popen|check_output|check_call)\s*\([^)]*shell\s*=\s*Truez-# safe|# trusted|# no user input|shlex\.quotezCUse shell=False with a list of args, or sanitize with shlex.quote())r6   r7   rA   zEP-034zos.system usagezDos.system() runs commands in a shell and is vulnerable to injection.z\bos\.system\s*\(z# safe|# trustedz=Use subprocess.run() with shell=False and a list of argumentszEP-035zUnsafe deserializationzGpickle/shelve/yaml.load can execute arbitrary code from untrusted data.zu(?:pickle\.loads?\s*\(|shelve\.open\s*\(|yaml\.load\s*\([^)]*(?:Loader\s*=\s*yaml\.(?:Unsafe|Full)Loader|(?!Loader)))z$# trusted|SafeLoader|yaml\.safe_loadzMUse yaml.safe_load(), json, or a safe serialization format for untrusted datazEP-036zInsecure HTTP URLzDUsing http:// instead of https:// transmits data without encryption.zV["']http://(?!localhost|127\.0\.0\.1|0\.0\.0\.0|::1|\[::1\]|example\.com|example\.org)z:# http ok|# local|# test|redirect.*https|upgrade.*insecurez@Use https:// for all external URLs to ensure encrypted transport)r6   r7   r:   r;   r>   lock	CHANGELOGzEP-037z!Permissive CORS (wildcard origin)z@Allow-Origin: * with credentials exposes the API to any website.zX(?:Access-Control-Allow-Origin|allow_origins|cors_origins|CORS_ORIGIN)\s*[:=]\s*["'*]?\*z.# dev only|# local|development|localhost|DEBUGz?Restrict CORS to specific trusted origins instead of wildcard *)r6   r:   r;   zEP-038zPath traversal riskzUJoining user input into file paths without validation enables path traversal attacks.zT(?:os\.path\.join|Path)\s*\([^)]*(?:request\.|user_|input|param|query|args\[|form\[)z?resolve\(\)|realpath|abspath|# validated|# safe|secure_filenamezSValidate paths with Path.resolve() and check they stay within the allowed directoryzEP-039zHardcoded JWT/signing secretzKJWT secrets in source code can be extracted to forge authentication tokens.zK(?:SECRET_KEY|JWT_SECRET|SIGNING_KEY|jwt_secret)\s*[:=]\s*["'][^"']{8,}["']zOexample|placeholder|test|mock|change.?me|your[_-]|TODO|os\.environ|getenv|env\(zDLoad signing secrets from environment variables or a secrets manager)r6   r7   r:   r;   .env.examplezEP-040zDynamic code execution (JS/TS)zLeval() and new Function() execute arbitrary code and are frequent RCE sinks.z\b(?:eval|new\s+Function)\s*\(z/eslint-disable|# safe|# trusted|webpack|bundlerzLRefactor to avoid dynamic code execution. Use JSON.parse() for data parsing.zEP-041zNode.js command injection sinkzMchild_process.exec() runs commands in a shell and is vulnerable to injection.z%child_process\.(?:exec|execSync)\s*\(z)# safe|# trusted|# no user input|execFilezLUse child_process.execFile() or spawn() with an args array instead of exec()zEP-042z!JWT signature verification bypasszIDecoding JWT tokens without verifying the signature allows token forgery.z_jwt\.decode\s*\([^)]*(?:verify\s*=\s*False|options\s*=\s*\{[^}]*"verify_signature"\s*:\s*False)z'# intentional|# public claims only|testzJAlways verify JWT signatures: jwt.decode(token, key, algorithms=['HS256'])zEP-042bzJWT 'none' algorithm (JS/TS)zJAllowing the 'none' algorithm in JWT verification permits unsigned tokens.z%algorithms\s*:\s*\[[^\]]*['"]none['"]z# intentional|testzRNever allow the 'none' algorithm in JWT verification. Specify explicit algorithms.zEP-043z%TLS certificate verification disabledzLDisabling TLS verification exposes connections to man-in-the-middle attacks.z;(?:verify\s*=\s*False|CERT_NONE|check_hostname\s*=\s*False)z,# dev only|# localhost|# self-signed ok|testzFNever disable TLS verification in production. Use proper certificates.zEP-043bz!TLS verification disabled (JS/TS)zM(?:rejectUnauthorized\s*:\s*false|NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*['\"]?0)zEP-049z"Weak cryptographic hash (MD5/SHA1)zHMD5 and SHA1 are broken for security purposes (collisions demonstrated).zhashlib\.(?:md5|sha1)\s*\(z?# non-security|# checksum only|# fingerprint|# cache key|# etagzIUse hashlib.sha256() or hashlib.sha3_256() for security-sensitive hashing)r6   r7   rI   zEP-049bzWeak cryptographic hash (JS/TS)z&createHash\s*\(\s*['"](?:md5|sha1)['"]z7Use createHash('sha256') for security-sensitive hashingzEP-050z$Insecure randomness for security usezPUsing Math.random() or Python's random module for tokens/secrets is predictable.z7random\.(?:random|randint|choice|randrange|sample)\s*\(z?# non-security|# game|# shuffle|# display|# ui|# test|secrets\.zTUse secrets.token_hex() or secrets.token_urlsafe() for security-sensitive randomness)r6   r7   rA   seedr9   zEP-050bz,Insecure randomness for security use (JS/TS)zNMath.random() is not cryptographically secure and produces predictable values.zMath\.random\s*\(\s*\)z@# non-security|# game|# animation|# ui|# display|# test|crypto\.zUUse crypto.randomUUID() or crypto.getRandomValues() for security-sensitive randomness)rC   rD   rE   r:   rF   zEP-060zUnpinned dependency versionzaUsing '*' or 'latest' for dependency versions enables supply chain attacks via malicious updates.z**/package.jsonz#["']\s*:\s*["'](?:\*|latest|>=)["']z*peerDependencies|devDependencies.*optionalzBPin dependency versions exactly (e.g., '1.2.3') or use a lock filer:   zEP-061z"Sensitive data in error/log outputz[Logging passwords, tokens, or keys in error messages can leak credentials (OWASP A10:2025).zl(?:print|logger?\.\w+|logging\.\w+)\s*\([^)]*(?:password|passwd|secret|token|api_key|private_key|credential)z2# safe|mask|redact|\*\*\*|sanitize|test|debug onlyzFNever log sensitive values. Redact or mask credentials before logging.zEP-061bz*Sensitive data in error/log output (JS/TS)zd(?:console\.\w+|logger?\.\w+)\s*\([^)]*(?:password|passwd|secret|token|apiKey|privateKey|credential)zEP-062z"Swallowed exception (except: pass)zVCatching exceptions and doing nothing hides bugs and security issues (OWASP A10:2025).z%except\s*(?:\w+\s*)?:\s*\n\s*pass\s*$z&# intentional|# best effort|# optionalzLAt minimum log the exception. Silent failures hide security-relevant errors.zEP-063z!Debug mode / stack trace exposurezURunning in debug mode in production exposes stack traces, config, and internal paths.zM(?:DEBUG\s*=\s*True|app\.run\([^)]*debug\s*=\s*True|FLASK_DEBUG\s*=\s*["']?1)z<# dev only|# local|# test|if.*development|os\.environ|getenvzEUse environment variables for debug flags. Never hardcode DEBUG=True.)r6   r7   rO   zEP-090z&GitHub Action not pinned to commit SHAzZUsing tags like @v4 instead of full SHA lets attackers hijack the action via tag mutation.z**/.github/workflows/*.ymlz"uses:\s*[^@\s]+@(?![a-f0-9]{40}\b)z)actions/checkout@|actions/setup-|# pinnedzJPin actions to full commit SHA: uses: owner/action@abc123... (40-char hex)zEP-091z.Dangerous pull_request_target with PR checkoutzWChecking out PR head in pull_request_target runs untrusted code with write permissions.pull_request_targetz-# safe|# reviewed|ref:\s*\$\{\{\s*github\.shaz]Never checkout PR head code in pull_request_target workflows. Use pull_request event instead.zEP-092z)Overprivileged GitHub Actions permissionsuQ   write-all grants the GITHUB_TOKEN full write access — violates least privilege.zpermissions:\s*write-allz# required|# release workflowzZDeclare minimal permissions per job: permissions: { contents: read, pull-requests: write }zEP-093z(Remote script execution (curl pipe bash)z?Piping curl/wget output to bash executes untrusted remote code.z9(?:curl\s+[^|\n]+|wget\s+-qO-\s+\S+)\s*\|\s*(?:bash|sh)\bz)# trusted|# official installer|# verifiedz@Download the script first, verify its checksum, then execute it.r;   zEP-094z$Docker FROM uses mutable :latest tagz^Using :latest in Dockerfile means builds are not reproducible and may pull compromised images.z**/Dockerfile*z^\s*FROM\s+\S+:latest(?:\s|$)z# ok|# dev only|AS\s+builderzIPin to a specific version and digest: FROM python:3.12-slim@sha256:abc...zEP-095zDocker container runs as rootzORunning as root inside containers exposes the host to container escape attacks.z^\s*USER\s+root\bz)# build stage|# install only|AS\s+builderzBAdd 'USER nonroot' or 'USER 1000' before the final CMD/ENTRYPOINT.zEP-096zKubernetes privileged containeruM   Privileged containers have full host access — effectively root on the node.z**/*.{yml,yaml}z^\s*privileged:\s*true\bz(# required|# debug only|# init containerz^Remove privileged: true. Use specific capabilities instead: capabilities: { add: [NET_ADMIN] }zEP-097z+Kubernetes allowPrivilegeEscalation enabledzPAllows processes inside the container to gain more privileges than their parent.z&^\s*allowPrivilegeEscalation:\s*true\bz# required|# init containerz7Set allowPrivilegeEscalation: false in securityContext.zEP-098z Kubernetes hostPath volume mountu`   hostPath volumes expose host filesystem to the container — path traversal and data theft risk.z^\s*hostPath:z*# required|# /dev/|# socket|type:\s*Socketz:Use PersistentVolumeClaim or emptyDir instead of hostPath.zEP-099z/Potential SSRF (user-controlled URL in request)z_Passing user input directly as a URL to HTTP clients enables SSRF attacks on internal services.zV(?:requests|httpx|urllib)\.\w+\s*\([^)]*(?:request\.\w+|params\[|args\[|user_|input\()z2# validated|# allowlisted|urlparse|validators\.urlz^Validate and allowlist URLs before fetching. Block internal IPs (169.254.x.x, 10.x.x.x, etc.).zEP-099bzPotential SSRF (JS/TS)zIPassing user input directly as a URL to fetch/axios enables SSRF attacks.zH(?:fetch|axios\.\w+)\s*\([^)]*(?:req\.(?:query|body|params)|user_|input)z,# validated|# allowlisted|new URL|url\.parsezNValidate and allowlist URLs before fetching. Block internal/private IP ranges.zEP-100z)Mass assignment (unfiltered request body)zePassing the full request body to ORM create/update allows attackers to set any field (e.g., isAdmin).zP(?:\.create|\.update|\.filter)\s*\(\s*\*\*(?:request\.data|request\.json|kwargs)z0# validated|serializer|schema|form\.cleaned_datazJExplicitly list allowed fields. Use serializers/schemas to validate input.zEP-100bzMass assignment (JS/TS)zQPassing req.body directly to ORM create/update allows attackers to set any field.z1(?:\.create|\.update|\.insert)\s*\(\s*req\.body\bz(# validated|schema|validator|zod|joi|yupzADestructure only allowed fields: const { name, email } = req.bodyzEP-101z/Insecure .NET deserialization (BinaryFormatter)zXBinaryFormatter can execute arbitrary code from untrusted data. Deprecated by Microsoft.z**/*.csz\bBinaryFormatter\bz# trusted|# legacy ok|obsoletezQUse System.Text.Json or JsonSerializer. BinaryFormatter is officially deprecated.zEP-102z1Insecure Java deserialization (ObjectInputStream)zRJava native deserialization can execute arbitrary code via crafted object streams.z	**/*.javaz-(?:new\s+ObjectInputStream|\.readObject\s*\()z7# trusted|ObjectInputFilter|ValidatingObjectInputStreamzSUse JSON/Protocol Buffers instead, or add ObjectInputFilter for class allowlisting.zEP-103zInsecure PHP deserializationzOPHP unserialize() can trigger __wakeup/__destruct for arbitrary code execution.z**/*.phpz\bunserialize\s*\(z%# trusted|allowed_classes\s*=>|# safezkUse json_decode() instead, or pass allowed_classes option: unserialize($data, ['allowed_classes' => false])zEP-104zInsecure Ruby deserializationzOMarshal.load and YAML.load can execute arbitrary Ruby code from untrusted data.z**/*.rbz3\b(?:Marshal\.load|YAML\.load|Psych\.unsafe_load)\bz%# trusted|safe_load|permitted_classesz4Use YAML.safe_load or JSON.parse for untrusted data.zEP-105z#LLM output passed to code executionzUExecuting LLM/model output as code or shell commands enables prompt injection to RCE.zo(?:subprocess|os\.system|exec|eval)\s*\([^)]*(?:response|completion|output|result|message|content|answer|reply)z(# sandboxed|# validated|# safe|test|mockz]Never execute LLM output directly. Validate, sandbox, or use structured tool calling instead.zEP-105bz+LLM output passed to code execution (JS/TS)zw(?:eval|new\s+Function|child_process\.exec)\s*\([^)]*(?:response|completion|output|result|message|content|answer|reply)zEP-106z$Untrusted input in LLM system promptzJInjecting user input into system prompts enables prompt injection attacks.zV(?:system|instructions?)\s*[:=].*(?:f["']|\.format\s*\(|%s|user_input|request\.|req\.)z5# sanitized|# validated|# escaped|xml_escape|templatezYIsolate user input in user messages with XML tags. Never interpolate into system prompts.)r6   r7   r:   rD   zEP-110zGo unsafe package usagezJThe unsafe package bypasses Go's type safety and memory safety guarantees.z**/*.goz'import\s+["']unsafe["']|unsafe\.Pointerz// cgo|// required|// ffizWAvoid unsafe unless interfacing with C code. Use encoding/binary for byte manipulation.r>   rC   zEP-111zGo open redirectzIUsing user input directly in http.Redirect enables open redirect attacks.z4http\.Redirect\s*\([^)]*r\.(URL|FormValue|Form\.Get)z// validated|// allowlistedz=Validate redirect URLs against an allowlist of trusted hosts.zEP-120z)dict.get() default bypassed by None valueu   dict.get('key', '').strip() crashes when key exists with None value — .get() only returns the default when the key is ABSENT, not when it's None.z}\.get\(\s*["'][^"']+["']\s*,\s*["'][^"']*["']\s*\)\s*\.\s*(?:strip|lower|upper|split|replace|startswith|endswith|format)\s*\(z,\bor\s+[\"']|if\s+\w+\s+is\s+not\s+None|noqau   Use (d.get('key') or '').strip() instead of d.get('key', '').strip() — the 'or' pattern handles both missing keys AND None valuesu^   dict.get('matrix_article', '').strip() → AttributeError: 'NoneType' has no attribute 'strip')
r	   r
   r   r   r   r   r   r   r   r   zEP-120bz2Object property default bypassed by null/undefinedzRobj.key || '' does not protect against null when using .trim()/.toLowerCase() etc.zq\.\w+\s*\|\|\s*["'][^"']*["']\s*\)\s*\.\s*(?:trim|toLowerCase|toUpperCase|split|replace|startsWith|endsWith)\s*\(z\?\?|noqa|// safezPUse nullish coalescing: (obj.key ?? '').trim() instead of (obj.key || '').trim()zEP-121z)Docker container memory limit below 512MBzContainers with mem_limit under 512MB may OOM-kill dev servers (Next.js Turbopack, webpack-dev-server, etc.) silently with exit code 0 and no traceback.z**/docker-compose*.{yml,yaml}z1mem_limit:\s*(?:12[0-8]m|192m|25[0-6]m|[1-9]\d?m)z3# production|# optimized|redis|postgres|minio|nginxu   Set mem_limit >= 512m for app containers, >= 2g for Node.js dev servers with Turbopack/webpack. Silent OOM kills show as exit code 0 with restart loops — check 'docker inspect' for OOMKilled.zVNext.js 16 Turbopack dev server OOM-killed at 256MB, crash-looped with no error outputzEP-122z/React state not included in API request payloadzState variable set via onChange/setState but never referenced in the fetch/post payload. The UI collects user input but silently discards it on submit.z-set\w+\s*\(\s*(?:\([^)]*\)\s*=>|prev\s*=>|\{)znoqa|// UI only|// display onlyzVerify every state variable populated by user input is included in the API request body. Search for the setter name and confirm its getter appears in the fetch/post call.zZcertidaoAssignments state collected from dropdown but never sent in finalize-multi payloadzEP-123z-HTTP 404 for optional resource instead of 204zReturning 404 for optional resources (thumbnails, avatars, previews) causes console errors and error tracking noise. Use 204 No Content for 'valid endpoint, no content available'.z(?:raise\s+HTTPException|return\s+JSONResponse)\s*\([^)]*(?:404|status_code\s*=\s*404)[^)]*(?:thumbnail|preview|avatar|icon|image)z# required|# must exist|noqazReturn 204 No Content instead of 404 for optional resources. Update frontend to handle: .then(r => r.ok && r.status !== 204 ? r.blob() : null)z]Thumbnail 404s flooding console after rolled-back transactions left ghost document referenceszEP-124z+Docker memswap_limit not matching mem_limitzWhen memswap_limit equals mem_limit, swap is effectively disabled. When memswap_limit is less than mem_limit, the container may behave unpredictably.zmemswap_limit:\s*(\S+)z# intentional|# no swap okzSet memswap_limit = 2x mem_limit to allow swap headroom, or omit to allow unlimited swap. Setting memswap_limit == mem_limit disables swap entirely, making OOM kills more likely.zEP-200zRCapacity bound multiplies count by unit instead of bounding the count (off-by-one)u~  A size guard of the form `count * UNIT > MAX_BYTES` rejects the final partial unit: an input of exactly MAX_BYTES needs ceil(MAX_BYTES/UNIT) units, so count*UNIT exceeds MAX_BYTES and the guard wrongly rejects a legitimate max-size object. Bound the count directly against ceil(MAX_BYTES/UNIT). Surfaced by mutation testing in media::decrypt_media — the symmetric form is the fix.z**/*.rsz7\w*count\w*\s+as\s+u64\s*\*\s*\w+\s+as\s+u64\s*>\s*MAX_z1div_ceil|# off-by-one ok|bound the count directlyu   Bound the count directly: `count > MAX_BYTES.div_ceil(UNIT)` — keeps encrypt/decrypt size limits symmetric so a legitimately-encrypted max-size object still decrypts.)r6   r7   z/tests/zEK-001zImport/Module not foundmodulenotfounderrorimporterrorzno module namedpythonz	EK-001-R1z"Missing dependency in requirementszIModule referenced in code but may be missing from requirements/pyproject.z^\s*(?:from|import)\s+(\w+)zOEnsure the module is installed and listed in requirements.txt or pyproject.toml)r	   r
   r   r   r   r   r   )r	   r
   r,   r-   r.   r/   zEK-002zAttribute/Type error on Noneattributeerrornonetype	typeerrornonez	EK-002-R1z*Missing None guard before attribute accesszHAccessing attributes on a value that may be None without checking first.z"(?:\.get\([^)]*\)|= None).*\.\w+\(z$if\s+\w+\s+is\s+not\s+None|if\s+\w+:zNAdd a None check before accessing attributes: if obj is not None: obj.method()z	EK-002-R2z"Function returning None implicitlyz?Functions without explicit return may return None unexpectedly.zFdef\s+\w+\s*\([^)]*\)\s*(?:->.*)?:\s*\n(?:(?:\s+.*\n)*?)(?=\ndef\s|\Z)zreturn\s+\S|->.*NonezPEnsure all code paths return a value, or add explicit '-> None' return type hintzEK-003z,SQLAlchemy async lazy-load (MissingGreenlet)missinggreenletz	lazy loadasyncz	EK-003-R1z6Missing selectinload/joinedload for async relationshipzQAccessing lazy-loaded relationships in async SQLAlchemy triggers MissingGreenlet.z](?:\.query|select)\s*\([^)]*\)(?!.*(?:selectinload|joinedload|subqueryload|lazyload|options))zAselectinload|joinedload|subqueryload|raiseload|lazy=['\"]selectinzNUse .options(selectinload(Model.relation)) for eager loading in async sessionszEK-004zDatabase connection refusedzconnection refused)
connectionrefused5432)r[   r\   3306databasez	EK-004-R1zHardcoded database host or portzPDatabase connection parameters hardcoded instead of using environment variables.z **/*.{py,ts,tsx,js,jsx,yml,yaml}zD(?:host|HOST)\s*[:=]\s*["'](?:localhost|127\.0\.0\.1|0\.0\.0\.0)["']zGos\.environ|getenv|process\.env|\.env|# default|# fallback|test|examplezRUse environment variables for database host/port to support different environmentsz	EK-004-R2zHardcoded database portz0Database port hardcoded instead of configurable.z1(?:port|PORT)\s*[:=]\s*(?:5432|3306|27017|6379)\bz9Use environment variables for database port configurationzEK-005zNull/undefined property accesszcannot read propertiesnull	undefinedzis not a functionbrowserz	EK-005-R1zMissing optional chainingzNProperty access chains without optional chaining (?.) crash on null/undefined.z\w+\.\w+\.\w+(?!\s*[?])z4\?\.|&&|!=\s*null|!==\s*null|!==\s*undefined|if\s*\(zCUse optional chaining: obj?.prop?.nested instead of obj.prop.nestedz	EK-005-R2z%Missing null guard before method callzBCalling methods on potentially null values without checking first.z=(?:getElementById|querySelector|find|get)\s*\([^)]*\)\s*\.\w+z\?\.|if\s*\(|&&|!==?\s*nullzRAdd null check or use optional chaining: element?.method() or if (element) { ... }zEK-006zReact hooks order violationzrendered fewer hooks)hooksorderzprevious renderreactz	EK-006-R1zConditional hook callzRHooks called inside if/else, loops, or after early returns violate Rules of Hooks.z(?:if\s*\([^)]*\)\s*\{[^}]*(?:useState|useEffect|useMemo|useCallback|useRef|useContext)|return\s+[^;]*;\s*\n\s*(?:const|let)\s+\[?\w+[,\]]?\s*=\s*use\w+)z# conditional ok|eslint-disablezPMove all hooks to the top level of the component, before any conditional returnszEK-007zReact hydration mismatch	hydration)serverclientmismatchz	EK-007-R1zBrowser-only API in render pathzJUsing window/document/localStorage during SSR causes hydration mismatches.zx(?:window\.|document\.|localStorage\.|sessionStorage\.|navigator\.)(?!.*(?:useEffect|componentDidMount|typeof\s+window))zFuseEffect|componentDidMount|typeof\s+window|'use client'|# client onlyzSWrap browser APIs in useEffect or check typeof window !== 'undefined' before accesszEK-008zCORS policy violationcorszaccess-control-allow-originz	EK-008-R1z(Permissive or missing CORS configurationzJWildcard CORS or missing CORS headers cause cross-origin request failures.zJConfigure CORS with specific allowed origins matching your frontend domainzEK-009zUnhandled promise rejection)	unhandledpromise	rejectionunhandledrejectionz	EK-009-R1zAsync call without catch/awaitzNPromise-returning calls without .catch() or try/await silently swallow errors.z	EK-009-R2z%Async event handler without try/catchzTAsync functions in event handlers without error handling cause unhandled rejections.z-Wrap async event handlers in try/catch blockszEK-011z!Container OOM kill (silent crash)	oomkilledz	exit code137)killedsignal9zout of memory)restartloop	containerzempty reply from serverdockerz	EK-011-R1z%Docker container memory limit too lowzNContainer mem_limit may be too low for the workload, causing silent OOM kills.zmem_limit:\s*\S+zCheck 'docker inspect <container> --format {{.State.OOMKilled}}' to confirm. Bump mem_limit (2g+ for Node.js dev, 1g+ for Python APIs). Also run 'docker stats' to see peak memory usage.z	EK-011-R2zNode.js heap limit not setzGNode.js may exceed container memory limit without --max-old-space-size.z/(?:node|next|npm|pnpm|yarn)\s+(?:dev|start|run)zmax-old-space-size|NODE_OPTIONSzXSet NODE_OPTIONS='--max-old-space-size=1536' to cap Node.js heap within container limitszEK-012z&dict.get() None value bypasses default)rU   rV   stripzhas no attributeloweruppersplitz	EK-012-R1z7dict.get() with string default chained to string methodztd.get('key', '').strip() fails when key exists with None value. dict.get() only uses the default when key is ABSENT.zb\.get\(\s*["'][^"']+["']\s*,\s*["'][^"']*["']\s*\)\s*\.\s*(?:strip|lower|upper|split|replace)\s*\(uZ   Use (d.get('key') or '').strip() — the 'or' pattern handles both missing AND None valueszEK-010zPermission denied / EACCESeacceszpermission deniederrno13systemz	EK-010-R1z+Hardcoded file path in restricted directoryzFWriting to system directories or paths requiring elevated permissions.z**/*.{py,ts,tsx,js,jsx,sh}zB(?:open|write|mkdir|chmod)\s*\([^)]*["']/(?:etc|usr|var|opt|root)/z)# root ok|# system|sudo|test|example|mockzZUse user-writable directories (e.g., /tmp, ~/.config, or $HOME) or check permissions firstz	EK-010-R2z,Missing directory creation before file writezMWriting files without ensuring parent directories exist causes EACCES/ENOENT.z$(?:open|write_text|write_bytes)\s*\(zCmkdir|makedirs|exist_ok|Path.*parent.*mkdir|os\.path\.exists|ensurez^Call os.makedirs(dir, exist_ok=True) or Path.mkdir(parents=True, exist_ok=True) before writing	KNOWLEDGEc                     t        j                  g dddt              } | j                  j	                         j                  d      D cg c]*  }|j	                         st        |j	                         z  , c}S c c}w )N)gitdiffz--cachedz--name-onlyz--diff-filter=ACMRT)capture_outputtextcwd
)
subprocessrunROOTstdoutrx   r{   )resultfs     r"   get_staged_filesr     s[    ^^H$DF '-mm&9&9&;&A&A$&GU1779D1779UUUs   A=!A=c                 6    t        |j                  |             S r1   )sortedrglob)glob_patternbases     r"   
find_filesr     s    $**\*++r!   c                 n    	 | j                  t              }|j                  |      S # t        $ r Y yw xY w)NF)relative_tor   
ValueErrormatch)filepathr   rels      r"   matches_globr     s<    ""4( 99\""  s   ( 	44c                    t        |       }|j                  r|j                  D ]
  }||v sg c S  	 | j                  dd      }g }|j	                  d      }t        j                  |j                        }t        |      D ]4  \  }}	|j                  |	      sd|j                   |	v sd|	v r/|j                  rst        d||j                  z
        }
t        t        |      ||j                  z   dz         }dj!                  ||
|       }t        j                  |j                  |      r	 t        | j#                  t$                    }|j)                  t+        |j                  |j,                  |j.                  ||dz   |	j1                         d d	 |j2                  |j4                  
             7 |S # t        $ r g cY S w xY w# t&        $ r t        |       }Y w xY w)Nutf-8replaceencodingerrorsr   znoqa: z	noqa: allr   r5   x   r%   r&   r   r'   r(   r)   r   r   )r   r   	read_textOSErrorr{   recompiler   	enumeratesearchr	   r   maxr   minlenjoinr   r   r   appendr$   r
   r   rx   r   r   )r   rulefilepath_strexclcontentfindingslinesregexir(   startendcontextrel_paths                 r"   	scan_filer     s   x=L&& 	D|#		$$gi$H HMM$EJJt||$EU# 4<<y!T)[D-@$$Aq4#5#556#e*a$*<*<&<q&@A))E%$4599T22G<)x33D9: OOG))UZZ\$3'	 	2 OA  	(  )x=)s#   F< 1G<G
	G
G$#G$c                    g }|rt               }nd }| D ]  }|r|j                  |k7  r|(|D cg c]  }t        ||j                        s| }}nHg }d|j                  v rt	        j
                  d|j                        }|r|j                  d      j                  d      D ]_  }	|j                  d |j                          |	z   |j                  |j                         d  z   }
|j                  t        |
t                     a n)|j                  t        |j                  t                     |D cg c]_  }dt        |      vrPdt        |      vrCdt        |      vr6dt        |      vr)d	t        |      vrd
t        |      vrdt        |      vr|a }}|D ]  }|j                  t        ||               |S c c}w c c}w )N{z\{([^}]+)\}r5   ,r=   r:   z.git/rF   rG   zbuild/r>   )r   r   r   r   r   r   groupr{   r   r   extendr   r   r   r   )r/   staged_onlyseverity_filterall_findingsfilesr   r   
rule_filesr   extexpandedr   s               r"   scan_codebaser     s   L " ;t}}?%*Nl1dnn.M!NJNJdnn$		.$..A${{1~33C8 F#'>>.5;;=#AC#G$..Y^YbYbYdYeJf#f"))*Xt*DEF !!*T^^T"BC%	 A."#a&03q6)3q6)3q6)CF*SV+ 	J 	 # 	;H	(D 9:	;3;8 1 O	s   GG6A$Gz!!!z!!!r4   r?   r<   z[91mz[93mz[90mz[0mc                 8   | syg g g d}| D ]   }||j                      j                  |       " dt        |        ddg}dD ]E  }||   }|st        |   }t        |   }|j                  d| d|j                          dt        |       dt                |j                  d	       |D ]  }|j                  d
| | t         d|j                   d|j                          |j                  d|j                   d|j                          |j                  d|j                          |j                  d|j                          |j                  r|j                  d|j                          |j                  d        H dj                  |      S )NzNo error patterns detected.r   u   ERROR PATTERN DETECTOR —  finding(s)<============================================================r   [z] (----------------------------------------z  z [z
    File: :z
    Code: z
    Fix:  z
    Ref:  r   )r   r   r   SEVERITY_ICONSSEVERITY_COLORSrz   RESETr%   r&   r'   r(   r)   r   r   r   )r   by_severityr   r   sevr   iconcolors           r"   format_textr   B  s   ,B7K *AJJ&&q)* &c(m_K@E ) C c"$r%#))+bUKwOPX 	ALL2eWTF5'AII;bNOLL:affXQqvvh78LL:affX./LL:ajj\23zzz!**67LL	  99Ur!   c                    t        j                  | D cg c]]  }|j                  |j                  |j                  |j
                  |j                  |j                  |j                  |j                  d_ c}d      S c c}w )Nr   r   indent)
jsondumpsr%   r&   r   r'   r(   r)   r   r   )r   r   s     r"   format_jsonr   _  sq    :: 	
  99[[JJJJJJ		
  	
s   A"A?c           
          t        d | D              }t        d | D              }t        d | D              }d| d| d| dt        |        d	S )	Nc              3   @   K   | ]  }|j                   d k(  sd  yw)r4   r5   Nr   .0r   s     r"   	<genexpr>z!format_summary.<locals>.<genexpr>s  s     ;QajjF&:q;   c              3   @   K   | ]  }|j                   d k(  sd  yw)r?   r5   Nr   r   s     r"   r   z!format_summary.<locals>.<genexpr>t  s     <AQZZ8%;a<r   c              3   @   K   | ]  }|j                   d k(  sd  yw)r<   r5   Nr   r   s     r"   r   z!format_summary.<locals>.<genexpr>u  s     9AQZZ5%8a9r   z
Patterns: z high, z	 medium, z low (z total))sumr   )r   r4   medr<   s       r"   format_summaryr   r  sW    ;(;;D
<<
<C
99
9CvWSE3%vc(m_GTTr!   c               #   j  K   ddl } t        t              j                         j                  dz  }|j                         syt        |j                  d            D ]  }|j                  j                  d      r	 | j                  j                  d|j                   |      }|rH|j                  r<| j                  j                  |      }|j                  j                  |       ||f  y# t         $ r5}t#        d|j                   d| t$        j&                  	       Y d}~d}~ww xY ww)
zYield (module, py_file) tuples for each non-underscore .py in error_knowledge/.

    Shared loader for both KNOWLEDGE (reactive) and PROACTIVE_PATTERNS (proactive).
    r   Nerror_knowledgez*.py_zerror_knowledge.z'Warning: Failed to load knowledge pack z: r'   )importlib.utilr   __file__resolveparentis_dirr   globr
   
startswithutilspec_from_file_locationstemloadermodule_from_specexec_module	Exceptionprintsysstderr)	importlibknowledge_dirpy_filerD   modexcs         r"   _iter_pack_modulesr  }  s
    
 N**,336GGM!-,,V45 #<<""3'
	#>>99"7<<.17D nn55d;'',7l"#  	#;GLL>C5Qzz# #	#s1   A9D3<A3C2/D32	D0;+D+&D3+D00D3returnc                      t        t              } t               D ]!  \  }}| j                  t	        |dg              # | S )a<  Load reactive knowledge base entries from error_knowledge/*.py modules.

    Each module may define a KNOWLEDGE list of ErrorKnowledge entries
    (matched against error messages via --analyze).
    Falls back to built-in KNOWLEDGE if directory doesn't exist.
    Returns the merged list (built-in + all packs).
    r   )r   r   r  r   getattr)mergedr  _py_files      r"   load_knowledge_packsr    s=     )_F+- 5Xgc;345Mr!   _proactive_patterns_cachec                      t         t         S g } t               D ]_  \  }}t        |dg       }|r:t        |t              s*t        d|j                   dt        j                         O| j                  |       a | a | S )u  Load proactive project-specific PatternRules from error_knowledge/*.py.

    Each module may define a PROACTIVE_PATTERNS list of PatternRule entries
    (scanned over the codebase on every commit, same way as the built-in
    PATTERNS list). Use this to add project-specific rules without forking
    detect_error_patterns.py.

    Cached per-process — re-importing modules is wasteful and rule lists
    are stable for the duration of a CLI invocation.
    PROACTIVE_PATTERNSzWarning: PROACTIVE_PATTERNS in z is not a list, skippingr   )
r  r  r  
isinstancer   r   r
   r   r   r   )patternsr  r  pack_patternss       r"   load_proactive_patternsr    s     !,((H*, 'W%92>M4!@3GLL>AYZzz#&' !)Or!   c                  $    t         t               z   S )zEBuilt-in PATTERNS + project-specific PROACTIVE_PATTERNS from plugins.)PATTERNSr  r    r!   r"   get_all_patternsr    s    -///r!   
error_textc                    g }t               }t        j                  d|       D ]X  }|j                  d      t	        |j                  d            }}||f}||vs7|j                  |       |j                  |       Z t        j                  d|       D ]o  }|j                  d      }t	        |j                  d            }t        j                  dd|      }||f}||vsN|j                  |       |j                  |       q t        j                  d|       D ]X  }|j                  d      t	        |j                  d            }}||f}||vs7|j                  |       |j                  |       Z t        j                  d| t        j                        D ]X  }|j                  d      t	        |j                  d            }}||f}||vs7|j                  |       |j                  |       Z |S )	zExtract file:line pairs from common stack trace formats.

    Supports: Python tracebacks, Node.js stack traces, browser console errors.
    Returns list of (filepath, line_number) tuples.
    z File\s+"([^"]+)",\s+line\s+(\d+)r5   r   z at\s+\S+\s+\(([^)]+):(\d+):\d+\)z^webpack:///\./r   z(at\s+(/[^:]+|[a-zA-Z]:\\[^:]+):(\d+):\d+zF(?:^|\s)((?:/[\w._-]+)+\.(?:py|js|ts|tsx|jsx|go|rb|java|cs|php)):(\d+))	setr   finditerr   r   addr   sub	MULTILINE)r  resultsseenr   r   linenokeys          r"   parse_stack_tracer!    s    G5D @*M   ;;q>3u{{1~+>& d?HHSMNN3  @*M  ;;q>U[[^$66,b(; d?HHSMNN3  H*U   ;;q>3u{{1~+>& d?HHSMNN3  QBLL   !;;q>3u{{1~+>& d?HHSMNN3  Nr!   error_lowerentryc                      t         fd|j                  D              ry|j                  D ]  }t         fd|D              s y y)zECheck if an error message matches an ErrorKnowledge entry's keywords.c              3   &   K   | ]  }|v  
 y wr1   r    r   kwr"  s     r"   r   z"_match_keywords.<locals>.<genexpr>  s     
62
6   Tc              3   &   K   | ]  }|v  
 y wr1   r    r&  s     r"   r   z"_match_keywords.<locals>.<genexpr>  s     3Rr[ 3r(  F)allr,   r-   )r"  r#  alt_sets   `  r"   _match_keywordsr,    sE     
6u~~
66%% 3733 r!   rootc                    g }t               }| j                         }t               }g }|D ]   }t        ||      s|j	                  |       " t        |       }|D ]+  }|j                  D ]  }	|r|D ]  \  }
}t        |
      j                         s||
z  n
t        |
      }|j                         s@t        ||	j                        sWt        ||	      D ]L  }|j                  |j                  |j                  f}||vs+|j!                  |       |j	                  |       N  t#        |	g      }|D ]L  }|j                  |j                  |j                  f}||vs+|j!                  |       |j	                  |       N  . |sg }t%        j&                  d|       D ]"  }|j	                  |j)                  d             $ t%        j&                  d|       D ]"  }|j	                  |j)                  d             $ t+               }|r|D ]  \  }
}t        |
      j                         s||
z  n
t        |
      }|j                         s@|D ]t  }	t        ||	j                        st        ||	      D ]L  }|j                  |j                  |j                  f}||vs+|j!                  |       |j	                  |       N v  |dd D ]N  }	 t-        j.                  dddd	d
dd|t1        |      g	ddd      }|j2                  j5                         j7                  d      D ]  }|j5                         st        |j5                               }|j                         r|D ]  }	t        ||	j                        rjt        ||	      D ][  }|j                  |j                  |j                  f}||vr"|j!                  |       |j	                  |       t9        |      dk\  s[ n t9        |      dk\  s n t9        |      dk\  s n t9        |      dk\  sO n |dd S # t,        j:                  t<        f$ r Y sw xY w)aj  Reactive error analysis. Feed an error message, get code pattern matches.

    1. Match error against knowledge base keywords
    2. If KB match: run associated scanning rules
    3. Extract file:line from stack traces, check those files first
    4. If no KB match: extract identifiers/quoted strings, grep codebase
    5. Deduplicate and cap at 50 results
    z['"]([^'"]{3,60})['"]r5   z2\b([A-Z][a-zA-Z0-9]+(?:Error|Exception|Warning))\bNrL   grepz-rnlz--include=*.pyz--include=*.tsz--include=*.tsxz--include=*.jsz--include=*.jsxTrB   )r   r   timeoutr   2   )r  ry   r  r,  r   r!  r/   r   is_absoluteexistsr   r   r   r'   r(   r%   r  r   r   r  r   r  r   r   r   r   rx   r{   r   TimeoutExpiredr   )r  r-  r   	seen_keysr"  	knowledgematched_entriesr#  stack_filesr   r   _linenor   findingr   broaderidentifiersr   all_patternsidentr   r(   s                         r"   analyze_errorr?    s.    #%L+.5I""$K %&I -/O *;.""5)*
 $J/K ! 1KK 	1D-8 =)L':>|:L:X:X:Ztl2`deq`rH(\(DNN-S'04'@ =G#*<<w"OC")3 )c 2 , 3 3G <	== $TF+G" 1||W\\7??Ci'MM#& ''0	1	11, !#[[!=zJ 	/Eu{{1~.	/[[!VXbc 	/Eu{{1~.	/ ())4 	A%g6:<6H6T6T6V4,.\`am\n??$ , A'$..A+4Xt+D A'.||W\\7??&S#&i#7$-MM#$6$0$7$7$@	AA	A !!_ 	E#V%57G&(8:KCI' $(dB	 #MM//177= "Dzz|#'

#5#??,(4 
*#/$..#I3<Xt3L %2/6||W\\7??.[+.i+?,5MM#,>,8,?,?,H+.|+<+B,1%2 $'|#4#:$)
* |,2!!"& < B&9	>  --w7 s,   A'P* B*P*+P*=P*P**QQlog_pathc                    	 | j                  dd      }t        j                  dt
        j                        }g }|j                  |      D cg c]  }|j                          }}|s|g}n^t        |      D ]P  \  }}	|dz   t        |      k  r||dz      n
t        |      }
||	|
 j                         }|s@|j                  |       R t        j                  d	t
        j                        }i }|D ]h  }|j                  |      st        j                   d
d|      }t        j                   dd|      }t        j                   dd|      }|dd }||vsd|||<   j i }|j#                         D ]0  \  }}t%        ||      }|s|j'                  d      d   dd }|||<   2 |S # t        $ r*}t        d| t        j                         i cY d}~S d}~ww xY wc c}w )zParse a log file for errors, deduplicate, analyze each unique one.

    Splits on common error boundaries (timestamps, 'ERROR', 'Traceback', etc.)
    Returns {error_signature: [findings]}
    r   r   r   zError reading log file: r   Nz[^(?:\d{4}[-/]\d{2}[-/]\d{2}[\sT]\d{2}:\d{2}|(?:ERROR|CRITICAL|FATAL|Exception|Traceback)\b)r5   z3(?:error|exception|traceback|failed|fatal|critical)z0x[0-9a-fA-F]+0xHEXz
\b\d{4,}\bNUMzline \d+zline N   r   r   r   )r   r   r   r   r   r   r   r  r  r   r   r   rx   r   
IGNORECASEr   r  itemsr?  r{   )r@  r-  r   r  boundary_reblocksm	positionsr   posr   blockerror_keywordsseen_signaturessigsig_keyr  r   display_keys                      r"   analyze_log_filerR  q  s   $$gi$H **	;
K F$/$8$8$ABqBIB	* 	%FAs&'!ec)n&<)AE"#g,CC$**,Ee$		% ZZ>
N ')O 
-$$U+ff&7ff]E3/ff[(C0ds)/)',OG$
- )+G%++- ,
U -++d+A.t4K#+GK , Nk  (.SZZ@	 Cs#   F% G%	G.GGGc                     dd l } | j                  d      }|j                  ddd       |j                  dg d	d
       |j                  ddd       |j                  ddd       |j                  ddd       |j                  dt        dd       |j                  dt        dd       |j                  ddd       |j	                         }|j
                  rt               }t               }t        ddddddd         t        d!       |D ]5  }t        |j                  dd|j                  dd|j                          7 t        d"t        |       d#t        t               d$t        |       d%       y|j                  rt               }t        dddd&d'dd d(dd)        t        d*       |D ]T  }d+j!                  |j"                        }t        |j                  dd|j$                  d'd|j                  d(d|        V t        d"t        |       d,       y|j&                  rt)        |j&                  t*              }	|j,                  rt        t/        |	             n|j0                  rt        t3        |	             n}|	rpt        d-t        |	       d.       t        d/|j&                  d d0  t        |j&                        d0kD  rd1nd2 d3       t        d4       t        t5        |	             nt        d5       t7        d6 |	D              rd7S dS |j8                  rt;        |j8                        }
|
j=                         st        d8|
 t>        j@                  9       y:tC        |
t*              }|j,                  r|jE                         D 	ci c]o  \  }}	||	D cg c]]  }|jF                  |jH                  |j                  |jJ                  |jL                  |jN                  |jP                  |jR                  d;_ c}q }}	}}t        t-        jT                  |d:<             n|j0                  r<tW        d= |jY                         D              }t        d>t        |       d?| d@       n|rtW        dA |jY                         D              }t        dBt        |       dC| dD       t        d4       |jE                         D ]2  \  }}	t        dE|        t        dF       t        t5        |	             4 nt        dG       t7        dH |jY                         D              }|rd7S dS t[        t               |j\                  |j                  I      }	|j,                  rt        t/        |	             n5|j0                  rt        t3        |	             nt        t5        |	             t7        dJ |	D              rd7S dS c c}w c c}}	}w )KNr   z&Scan codebase for known error patterns)r   z--staged
store_truezOnly scan staged files)actionhelpz
--severityr   zFilter by severity)choicesrV  z--jsonzJSON outputz	--summaryzOne-line summary onlyz--list-ruleszList all pattern rulesz	--analyze	ERROR_MSGzDReactive analysis: match an error message against the knowledge base)typemetavarrV  z--analyze-logLOG_PATHz7Parse a log file for errors and analyze each unique onez--list-knowledgez>List all ErrorKnowledge entries (id, name, category, keywords)IDz<8 Sevz<7Namez<------------------------------------------------------------r   z pattern rules registered (z built-in + z  from error_knowledge/ plugins).Categoryz<12z<45Keywordszd----------------------------------------------------------------------------------------------------z, z knowledge entries loaded.u   REACTIVE ANALYSIS — z finding(s) for error:z  "d   z...r   "r   z0No code patterns matched for this error message.c              3   :   K   | ]  }|j                   d k(    ywr4   Nr   r   s     r"   r   zmain.<locals>.<genexpr>  s     ?

f,?   r5   zError: Log file not found: r   r   r   r   c              3   2   K   | ]  }t        |        y wr1   r   r   fss     r"   r   zmain.<locals>.<genexpr>  s     ;BB;   zLog analysis: z unique errors, z total findingsc              3   2   K   | ]  }t        |        y wr1   rh  ri  s     r"   r   zmain.<locals>.<genexpr>  s     ?CG?rk  u   LOG ANALYSIS — z unique error(s), r   z
Error: r   z)No actionable patterns found in log file.c              3   H   K   | ]  }|D ]  }|j                   d k(     ywre  r   )r   rj  r   s      r"   r   zmain.<locals>.<genexpr>  s0      
%'R
@AAJJ& 
 
s    ")r   r   c              3   :   K   | ]  }|j                   d k(    ywre  r   r   s     r"   r   zmain.<locals>.<genexpr>  s     ;QAJJ&(;rf  )/argparseArgumentParseradd_argumentr   
parse_args
list_rulesr  r  r   r	   r   r
   r   r  list_knowledger  r   r,   r.   analyzer?  r   r   r   summaryr   r   anyanalyze_logr   r3  r   r   rR  rF  r%   r&   r'   r(   r)   r   r   r   r   valuesr   staged)ro  parserargs	all_rules	proactiverr6  ekkw_strr   r@  r  rO  r   json_outtotalhas_highs                    r"   mainr    sU   $$1Y$ZF

<>VW
.GNbc
MJ
L?VW
|BZ[
#{c  e
c:V  X
*<]  _D$&	+-	b	5*AfX./h 	9AQTT"IQqzz"oQqvvh78	93y>" #h-S^,<<\^ 	_(*	b	:c*!F3<qEFi 	JBYYr{{+FRUU2JaC0"''#axHI	J 	3y>""<=>|| t499+h'(\\.*+.s8}o=STUT\\$3/0#dll:Kc:QWY0ZZ\]^hk(+,HI?h??qFQF(() /z:L"8T299 &-]]_
 
 "C  & 	 $%991;;$%JJ !AJJAJJ 
H 
 $**Xa01\\;'..*:;;EN3w<.0@WX?gnn.>??)#g,7I%P[\]h%,]]_ 1MCIcU+,(O+h/01
 AB 
+2>>+;
 
 q#!#-/T[[Z^ZgZghHyyk(#$	nX&'k(#$;(;;1BBO
s   *W%7A"W W% W%__main__)FN)1r   r   r   r   r   dataclassesr   r   pathlibr   r   r   parentsr   r   r$   r+   r  r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r   tupler   r!  boolr,  r?  dictrR  r  r   exitr    r!   r"   <module>r     se  4  	  
 ( H~''* 6 6 6  	 	 	 # # #&R)NpecgR  -NKHH	!R8 !g!/G
9RR %^'1g:SRn 'le].	oRF )_`HnIGRb -b(89^UcR~ 4c([9ICRd 1e()NdUeR@ 0d*KRDAR\ o/7MG]Rx .i4Xk)yRT *f"K0eCURp -k(jgjCqRL +k?XKQMRh )b I EfQiRD 6h+ArRZER` 4h(BDSCaR| +nrPn9}R` 'hr@hAaRx /i(QGaPyRT	 'j(>GZCU	Rp	 #\cIV8q	RH
 Z$,P)I
Rd
 %] I@`8e
R@  ZmVSaAR\ 0VoJR7]Rx "kk[f8yRT +abkWQURp -b(1K_UqRL -c(8E_CMRh 0_vC]8iR@ +`(<.eCAR\ 4bNHY8]Rt 0b(`HYCuRP 1^-[\6QRh .^(=[JCiRD 3fJ[gKER\ ;d()\hL]RB *w#:FU%&CR^ 1q DNY8_Rv 9q({NYCwRR 1l8B_8SRn 0kdXX9oRR 5p.5E]
SRl =m.&Ip
mRF 8g.+9m
GR` 7ULES%v.aRD 3t"08\
ER^ ,e"$EU
_Rx .c#+Dq%v.yRT :f#97J%v.URp /v# FM%v.qRT >umNq8URl %_(_HaCmRH 8{gL]6IR` &g(HDTCaRD >n&:d
ER^ @h@Sf
_Rx +e%A~
yRR ,eFAG
SRt 2k GDp8uRL :k( ODpCMRh 3`mQlAiRD &`>5j &)ER` _G7P
aRz 8b UHNq{RZ Ah( I-c
[Rt 8\1DOuiuRT >U"@;emURt <o V8epuRT :V1)6lURz aN
 JMe4{Rx  &'($o(9':;9g#6j

( +"J/"F+,Af!#=!Hi
 9]#a!8k

B ;#$"G,-Mo#x!ei

, *&'79Z[6n!<c!km
 .N3P!kT

B -*F3%{3 !&!

 0l!02!X^
 <`!0X!?m

"J *();<,p* u!Ck

, '676h!0 T!jn

, $456?h!3w!Re

, *6+,-5l!0c!AQ
 <r!0J!LH

B 0% %,&'
 <l9+M
 1e!*J!Cs

$N 58+,!!!	
 NS# Bu	
4 )*+gt_=Bd!6a!Mu
 Ck#?!gy

o	V#	4 Vz
V,#(V$V  4<%JO:&U#6d>2  *. 4$; - 80$ 0
/# /$uS#X*? /d	 	^ 	 	fc f f$w- fR=t =4 =Dd7m9K4L =HdCN zCHHTV r!   