feat(security): SEC004 + SEC007 multi-language dispatch (PR #18)#18
Merged
Conversation
Closes the audit gaps identified after PRs #10 / #12. The audit across SEC003-008: | Rule | Coverage before | Action this PR | |---|---|---| | SEC003 TLS off | text-level Python/Node/Go/.NET | unchanged (decent already) | | SEC004 shell | Python `shell=True`+interp only | **language-aware dispatch** | | SEC005 SQL concat | text+string-literal Python/Java | unchanged (decent) | | SEC006 CORS | text-level cross-language | unchanged | | SEC007 JWT | `name.contains("jwt")` Python only | **language-aware dispatch** | | SEC008 deser | Python/Node/Java idioms | unchanged (decent) | ## SEC004 expansion Per-language shell-running idioms; requires interpolation in arg. - **Python**: subprocess.run/Popen with `shell=True` + interp - **Node.js**: `child_process.exec` / `execSync` with interp (always shells out, no `shell:true` gate; `execFile` is the safe one) - **PHP**: global `shell_exec` / `exec` / `passthru` / `system` / `proc_open` with interp - **Java**: `Runtime.getRuntime().exec(String)` overload with concat — String[] overload safe and excluded - **Go**: `exec.Command("sh"|"bash"|"/bin/sh"|"/bin/bash", "-c", ...)` with interp. Bare `exec.Command("ls", arg)` (argv-style) excluded — no shell metachar interpretation `text_has_interp` extended with PHP `.` concat (gated on `$` to avoid floating-point literals). ## SEC007 expansion Per-language JWT decode without verification: - **Python**: `jwt.decode(...)` without algorithms/key/verify kwarg (existing behaviour) - **Node.js**: `jsonwebtoken.decode()` always returns unverified claims — flag unconditionally; `verify(token, secret, opts)` is the safe API. `verify()` with `verify: false` opt also flagged. - **Java / Kotlin**: Auth0 lib's `JWT.decode(token)` returns unverified DecodedJWT; safe path is `JWT.require(...).build().verify(token)`. - **PHP**: firebase/php-jwt's `JWT::decode($token, $key)` requires explicit algorithm list. Flagged unless one of `'HS256'`/`'RS256'`/ `'ES256'`/`'EdDSA'` appears in call text. Algorithm-`none` detection extended with JWT-spec literal `"alg": "none"` shape. `check_jwt_unsafe` now takes `&ParsedFile` so language identity is available — prevents PHP `JWT::decode` from being misclassified as Java (the old `name.contains("JWT")` check was language-blind). ## Infrastructure changes 1. **`call_name` extended for PHP scoped/member calls.** Previously only handled Java's `method_invocation`; now also composes `Class::method` from `scoped_call_expression` and `$obj->method` from `member_call_expression`. 2. **`leaf_method_name(name)` helper** — splits on `.` / `::` / `->` so `JWT::decode`'s leaf is `decode`, not the whole string. 3. **walk dispatch** extended with `scoped_call_expression` and `member_call_expression` node kinds. ## Tests 10 new (5 SEC004 multi-lang + 5 SEC007 multi-lang). 174 → **177** total tests passing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5 tasks
wei9072
added a commit
that referenced
this pull request
May 7, 2026
…#19) (#19) Continues PR #18's audit. SEC003/005/008 already had decent multi-language coverage, but each had specific gaps that surface in real-world auth / DB / serialization code. ## SEC003 — TLS verification disabled Added detection for: - **PHP curl**: \`CURLOPT_SSL_VERIFYPEER => false / 0\` and \`CURLOPT_SSL_VERIFYHOST => 0\`. Both positional (\`curl_setopt($ch, OPT, 0)\`) and inline-array (\`curl_setopt_array($ch, array(OPT => false))\`) forms. Deferred-via-variable form intentionally not caught (would need dataflow). - **Ruby OpenSSL**: \`VERIFY_NONE\` / \`OpenSSL::SSL::VERIFY_NONE\`. - **Java**: \`NoopHostnameVerifier\` / \`TrustAllHostnameVerifier\` class names. Doesn't catch arbitrary anonymous-inner-class trust managers — those need real flow analysis. ## SEC005 — SQL string concat Receiver-method matcher expanded: | Family | Methods added | |---|---| | Python | (existing: execute, executemany) | | JDBC | + executeLargeUpdate, executeBatch | | Node.js / Go | + Query, QueryRow, QueryContext, Exec, ExecContext | | PHP PDO | + exec, prepare | | Ruby | + find_by_sql | | PHP global | + mysqli_query, mysql_query, pg_query, sqlite_query | Plus: - \`leaf_method_name\` used (handles PHP \`::\` / \`->\`). - \`text_has_interp\` (introduced in PR #18) replaces SEC005's Python-shaped local check, picking up PHP \`.\` concat correctly. - \`contains_sql_in_string_literal\` extended to recognize PHP's \`encapsed_string\` / \`string_value\` and heredoc / template literal kinds, so the SQL-keyword gate sees the string content. ## SEC008 — insecure deserialization Dangerous-call list extended: | Language | Added | |---|---| | Python | + shelve.open, dill.loads, dill.load | | Java | + XMLDecoder.readObject | | **PHP** | + unserialize (textbook unsafe path) | | **Ruby** | + Marshal.load, YAML.load, Oj.load | | **C# / .NET** | + BinaryFormatter.Deserialize, SoapFormatter.Deserialize, NetDataContractSerializer.ReadObject | | Go | + gob.NewDecoder | ## Tests 7 new (SEC003 PHP curl ×3, SEC005 Go db.Query + PHP mysqli_query, SEC008 PHP unserialize + C# BinaryFormatter). 184 → 191 total tests pass. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the audit gaps in SEC003-008 noted after PRs #10 / #12.
Two rules had real coverage gaps; the others were already decent.
SEC004 expansion
`text_has_interp` extended with PHP `.` concat (gated on `$` to
avoid floating-point literals).
SEC007 expansion
`check_jwt_unsafe` now takes `&ParsedFile` so language identity
is available — prevents PHP `JWT::decode` from being misclassified
as Java (the old `name.contains("JWT")` check was language-blind).
Infrastructure
`scoped_call_expression` (`Class::method`) and
`member_call_expression` (`$obj->method`). Previously these
fell back to `named_child(0)` and returned just the receiver.
`->` so leaf-method matching works across all four conventions.
`member_call_expression` node kinds.
Tests
10 new tests (5 SEC004 + 5 SEC007 covering Python / Node / PHP /
Java / Go). 174 → 177 total tests pass.
Test plan
(each only fires on its own language)
(positive negative)
🤖 Generated with Claude Code