Repository navigation
ACOMMONS-231 Add IpAddressCandidateExtractor.extractAll to find embedded IP address candidates - #596
Conversation
…ded IP address candidates `extract` interprets the whole literal as one address, endpoint, URL, or list and returns only the first candidate from its beginning, so addresses in path segments or surrounding text, such as "/registry/ipaddresses/192.168.2.3", are missed. `extractAll` scans the whole content for address-shaped tokens and returns every candidate in order of appearance. It strips brackets and ports, preserves zones and bare CIDR suffixes, treats URL hosts' slash suffixes as paths, and skips tokens embedded in words, dotted sequences (OIDs), hyphenated names, user information, and User-Agent product versions such as "Chrome/120.0.0.0". `extract` keeps its existing behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses inside "//userinfo@" spans, such as "192.0.2.8" in "http://192.0.2.8:password@example.org/path/10.0.0.1", are credentials rather than hosts. The URL host, path, query, and fragment are still searched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the lookaround-heavy regular expression (complexity 46) with a scan of hex/colon/dot runs whose boundaries and shapes are checked in small helpers, and split complex methods. Reverse DNS names such as "1.2.0.192.in-addr.arpa" remain excluded, and the jar stays within the enforced size limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
addIpv4Candidates searched the rest of the content for the next colon for every address-shaped run, making extraction quadratic for many colon-free addresses. Scan each run's characters once instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…etters before runs An IPv6 address followed by a single colon used as punctuation, as in "Server ::1: connection refused", is now found, like its IPv4 counterpart. The left boundary decodes the full code point before a run, so a supplementary-plane letter, as in "𠀋192.0.2.1", embeds the address in a word. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tive complexity Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code Review 👍 Approved with suggestions 2 closed / 3 findings🟡 Medium risk · The new extractAll heuristic returns embedded address-shaped tokens without validation, so consumers could classify version numbers as IP candidates. Adds 💡 Cross-PR: Combined Commons JAR likely exceeds the 190,000-byte enforcer limit · #593📄 commons/src/main/java/org/sonarsource/analyzer/commons/appsec/IpAddressCandidateExtractor.java:153 Breaks when this PR and #593 (together with its base stack) both land on master, and How
Fix
Compared ✅ 2 closed✅ Edge Case: IPv6 address followed by a colon yields no candidate
✅ Edge Case: Left boundary check ignores supplementary-plane letters
🤖 Prompt for agentsVerification Rules 2/2 met Repos 11 inspected · PRs 1 inspectedFunctional validation No results Rules 2 passed 1 not applicable
Broader impact Repos 11 inspected PRs 1 inspectedCross-repo 11 repositories inspected 0 open impacts
6 more repositories Cross-PR 1 PR inspected 1 open impact
Auto-approval Not enabledFor more verification details, comment OptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
|




ACOMMONS-231
Problem
IpAddressCandidateExtractor.extractinterprets a whole literal as a single address, endpoint, URL, or list and returns one candidate from its beginning. Addresses later in the string are missed, e.g."/registry/ipaddresses/192.168.2.3"(Kubernetes),"connect to 10.0.0.1", or every entry after the first in"10.0.0.1;10.0.0.2".Contract decision
extract(String)is unchanged. It is public API for consumers that expect the whole literal to be one address (e.g. firewall-rule values) and rely on malformed content staying observable as an unparseable candidate. Changing it to search would hide that.extractAll(String): List<String>scans the whole content and returns all address-shaped candidates in order of appearance (duplicates retained, unmodifiable list).::, 8 groups or fewer with::, optional dotted IPv4 tail, optional zone). They are not range-validated (999.1.1.1,/33are returned, likeextract), so consumers still callIpAddressClassifier.parse.//host,user@host), where they are paths. This matchesextract._or-, or inside longer dotted or colon sequences, are ignored (v1.2.3.4, OIDs1.3.6.1.4.1,host-1.2.3.4, times12:30:45, MAC addresses,std::vector). Addresses in URL user information (http://192.0.2.8:pass@host/...,//user:1.2.3.4@host) or directly followed by@are ignored. The URL host, path, query, and fragment are still searched. User-Agent product versions such asChrome/120.0.0.0are also ignored. A trailing full stop or single colon (punctuation) ends a candidate. Dotted names such as1.2.0.192.in-addr.arpaare ignored. Boundaries are Unicode-aware, including supplementary-plane letters.extractyields a parseable address,extractAllcontains it.Known limitations (documented)
version 1.0.0.0) are returned. Consumers classify them like any literal.10.0.0.1-10.0.0.9) yield no candidate, because hyphens join tokens. Ranges with spaces work.name/1.2.3.4at the start of the content or after whitespace is treated as a product token and skipped.Tests
mvn clean install -Pcoveragepasses, includingenforce-plugin-size. The Commons JAR is 186,408 bytes at base and 189,601 bytes on this branch (max 190,000). The extractor has 100% line coverage. A 100k-address regression test guards against quadratic scanning without asserting on timing.🤖 Generated with Claude Code