@clear-capabilities/agentic-security-scanner 0.147.5 → 0.148.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -0
- package/bin/agentic-security.js +47 -15
- package/dist/agentic-security.mjs +2 -2
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/nist-800-171-r3.json +1269 -0
- package/package.json +3 -3
- package/src/engine.js +37 -4
- package/src/posture/auditor-walkthrough.js +89 -6
- package/src/posture/compliance-frameworks/nist-800-171-r3.json +1269 -0
- package/src/posture/threat-model.js +20 -3
- package/src/sast/python-sinks.js +24 -1
|
@@ -32,7 +32,13 @@ const ASSET_PATTERNS = [
|
|
|
32
32
|
// [regex, category, exposure]
|
|
33
33
|
[/(?:stripe|paddle|braintree)\.\w+\.create/g, 'payment-method', 'public-api'],
|
|
34
34
|
[/(?:User|users?)\.create\(|prisma\.user\.create/g, 'identity', 'public-api'],
|
|
35
|
-
|
|
35
|
+
// Real customer false positive: `session = requests.Session()` matched —
|
|
36
|
+
// the pattern only checked the LHS identifier, so an HTTP CLIENT object
|
|
37
|
+
// (requests.Session / aiohttp.ClientSession / httpx.Client) got reported
|
|
38
|
+
// as an "authenticated user session" asset. The negative lookahead
|
|
39
|
+
// excludes the specific RHS constructor shapes that mean "this is an
|
|
40
|
+
// outbound HTTP client", not "this holds a logged-in user's session".
|
|
41
|
+
[/(?:session|token|jwt)\s*=\s*(?!\s*(?:requests\.Session|aiohttp\.ClientSession|httpx\.(?:Client|AsyncClient))\s*\()/g, 'session', 'internal'],
|
|
36
42
|
[/process\.env\.([A-Z_]+(?:KEY|SECRET|TOKEN))/g, 'secret', 'internal'],
|
|
37
43
|
[/(?:aws-sdk|@aws-sdk).*\.upload|s3\.put/g, 'object-storage', 'public-api'],
|
|
38
44
|
[/(?:openai|anthropic|together|groq)\.(?:chat|messages|completions)/g, 'llm-egress', 'external-api'],
|
|
@@ -43,8 +49,19 @@ const TRUST_BOUNDARY_PATTERNS = [
|
|
|
43
49
|
[/router\.(?:get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/g, 'http-route'],
|
|
44
50
|
[/@(?:Get|Post|Put|Patch|Delete)Mapping\s*\(/g, 'http-route-java'],
|
|
45
51
|
[/@(?:app|router)\.(?:get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/g, 'http-route-py'],
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
// Real customer false positive: alternation without a shared group scopes
|
|
53
|
+
// ONLY the first branch to a queue library — `(?:kafka|pubsub|sqs|sns)\.produce
|
|
54
|
+
// |\.publish|\.sendMessage` is three TOP-LEVEL alternatives, and the last
|
|
55
|
+
// two are bare, unscoped substrings. `bundle.publisher_domain` and
|
|
56
|
+
// `bundle.published_at` both contain the literal substring ".publish" (the
|
|
57
|
+
// first 8 characters of "publisher"/"published"), so plain field access
|
|
58
|
+
// with no queue library, and no method call at all, was read as a
|
|
59
|
+
// queue-producer trust boundary. Moving the method-name alternation
|
|
60
|
+
// INSIDE the shared `(?:kafka|pubsub|sqs|sns)\.` prefix scopes every
|
|
61
|
+
// branch to an actual queue client, and requiring a trailing `(` excludes
|
|
62
|
+
// attribute access from matching a call-shaped rule.
|
|
63
|
+
[/(?:kafka|pubsub|sqs|sns)\.(?:consume|subscribe|receiveMessage)\s*\(/gi, 'queue-consumer'],
|
|
64
|
+
[/(?:kafka|pubsub|sqs|sns)\.(?:produce|publish|sendMessage)\s*\(/gi, 'queue-producer'],
|
|
48
65
|
[/grpc\.Server|new\s+Server\s*\(\s*\)/g, 'grpc-server'],
|
|
49
66
|
[/(?:db|pool|client)\.(?:query|execute|raw)\s*\(/g, 'db-edge'],
|
|
50
67
|
];
|
package/src/sast/python-sinks.js
CHANGED
|
@@ -41,7 +41,30 @@ const SQLA_RAW_EXEC_CONCAT_RE = /\b(?:cursor|conn|connection|session)\s*\.\s*exe
|
|
|
41
41
|
// quotes (single quotes inside double-quoted f-string and vice versa) so we
|
|
42
42
|
// use two parallel patterns rather than a single character class that excludes
|
|
43
43
|
// both quote kinds.
|
|
44
|
-
|
|
44
|
+
//
|
|
45
|
+
// Two real-world false positives, both from the same underlying defect —
|
|
46
|
+
// this regex checked for a co-occurring keyword-shaped substring and a `{}`
|
|
47
|
+
// interpolation, not an actual SQL statement being assigned:
|
|
48
|
+
//
|
|
49
|
+
// query = f"SELECT * FROM users WHERE id = {user_id}" -> should fire
|
|
50
|
+
// print(f"[{position}] {len(selected)} readable pages") -> was firing
|
|
51
|
+
// raise RuntimeError(f"Selected TAR members exceed {N} bytes") -> was firing
|
|
52
|
+
//
|
|
53
|
+
// Case-insensitive matching with no word boundary let the SQL keyword
|
|
54
|
+
// alternation match inside ordinary English/identifier text — "Select" is
|
|
55
|
+
// the first six letters of "Selected", and "select" is the first six of the
|
|
56
|
+
// variable name "selected". `\b...\b` fixes that: a word boundary requires a
|
|
57
|
+
// non-word/word transition, which does not exist between "Select" and the
|
|
58
|
+
// trailing "ed" (both are word characters), so "Selected"/"selected" no
|
|
59
|
+
// longer match while "SELECT"/"select" as complete tokens still do.
|
|
60
|
+
//
|
|
61
|
+
// The vuln title claims "f-string SQL ASSIGNED TO VARIABLE", but the regex
|
|
62
|
+
// never actually checked for an assignment — it fired on any f-string
|
|
63
|
+
// anywhere, including a bare argument to print()/raise. Requiring
|
|
64
|
+
// `IDENT = ` (optionally through an opening paren, for a wrapped assignment)
|
|
65
|
+
// immediately before the f-string makes the check match what its own name
|
|
66
|
+
// promises, and excludes the print()/raise-message shape entirely.
|
|
67
|
+
const SQLA_FSTRING_SQL_ASSIGN_RE = /\b[A-Za-z_]\w*\s*=\s*\(?\s*(?:f"[^"]*\b(?:SELECT|INSERT|UPDATE|DELETE)\b[^"]*\{[^}]*\}|f'[^']*\b(?:SELECT|INSERT|UPDATE|DELETE)\b[^']*\{[^}]*\})/gi;
|
|
45
68
|
|
|
46
69
|
// ─── Command injection ────────────────────────────────────────────────────
|
|
47
70
|
//
|