Skip to content

JS-2774 S5332: Add the identifier URL inventory and matcher - #8279

Closed
ZiadMarey wants to merge 1 commit into
masterfrom
zm/JS-2774
Closed

ZiadMarey wants to merge 1 commit into
masterfrom
zm/JS-2774

Conversation

@ZiadMarey

@ZiadMarey ZiadMarey commented Oct 29, 2026 •

Copy link
Copy Markdown

Summary by Gitar

  • Identifier inventory:
    • Loads shared host-to-path-pattern data from Analyzer Commons JSON into a Map of compiled regexes
  • Non-matching edge cases:
    • Rejects ports, userinfo, and backslashes so lookalike or malformed URLs are not treated as identifiers
  • Empty inventory:
    • Shared identifier-urls.json is currently empty, so isKnownIdentifierUrl returns false for every URL

This will update automatically on new commits.

@ZiadMarey
ZiadMarey requested a balanced review from Copilot and removed request for Copilot October 29, 2026 15:29
@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

JS-2774

Comment on lines +21 to +35
['adnovum.ch', /^\/nevisidm\/ws/],
['api.onedoc.ch', /^\/(?:LocationServiceType|profession)/],
['archway.archre.com', /^\/user_profile_id/],
['auth0.com', /^\/oauth\/grant-type/],
['big.arvato-infoscore.de', /^\/BIG_V2/],
['bigcontent.io', /^\/cms\/schema/],
['cloud.gbtec.de', /^\/rels/],
['cv.iptc.org', /^\/newscodes\/digitalsourcetype/],
['dashif.org', /^\/(?:guidelines|thumbnail_tile)/],
['data.europa.eu', /^\/(?:esco|p27)/],
['dbpedia.org', /^\/(?:ontology|property|resource)/],
['diaceutics.com', /^\/roles/],
['elimu.io', /^\/careplan\/disableSMS/],
['experian.com', /^\/communicationV2\/schemas/],
['g-p.com', /^\/schema/],

@gitar-bot gitar-bot Bot Oct 29, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: Path prefixes have no segment boundary, so unrelated paths match

Every path regex is an open prefix such as /^\/fhir/, /^\/rels/, /^\/sct/ or /^\/schema/, with nothing checking where the path segment ends. As a result, http://hapifhir.io/fhir-proxy, http://cloud.gbtec.de/release-notes, http://snomed.info/sctapi and http://dbpedia.org/resources-api are all treated as identifiers, and their cleartext URLs would go unreported. The tests do check ? and # as boundaries (/GPX?version=1#section), which suggests segment-level matching is what's intended.

Fix: put a boundary after each prefix, e.g. (?=[/?#]|$). Alternatively, make the boundary part of the matcher so individual entries don't each need it.

Require that the matched prefix ends at a path, query or fragment boundary (or the end of the string), except for whole-host '/' entries.:

const suffix = remainder.startsWith('/') ? remainder : `/${remainder}`;
const pattern = IDENTIFIER_URLS.get(match[1].toLowerCase());
if (!pattern) {
  return false;
}
const m = pattern.exec(suffix);
return m !== null && (m[0] === '/' || /^(?:[/?#]|$)/.test(suffix.slice(m[0].length)));

Was this helpful? React with 👍 / 👎

@sonarqube-next

Copy link
Copy Markdown

@vdiez

vdiez commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

if this needs to be in sync with analyzer commons, let's remove it from SonarJS and add rely on it. analzyer-commons already publishes an npm package that SonarJS depends on. Let's keep a single source of truth

* along with this program; if not, see https://sonarsource.com/license/ssal/
*/

import identifierUrls from '@sonarsource/analyzer-commons-configurations/identifier-urls.json' with { type: 'json' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Bug: Imported identifier-urls.json is not in any published Commons package

This commit swaps the inline map for import identifierUrls from '@sonarsource/analyzer-commons-configurations/identifier-urls.json'. The root package.json still pins 2.31.0-5284, and that tarball contains only README.md, secret-patterns.json and secret-exclusion-corpus.json. I unpacked every published version (2.32.0-5319, 2.33.0-5369, 2.34.0-15495, 2.35.0-15509) and none of them ships identifier-urls.json. The files whitelist in the latest package.json also leaves it out. So the TypeScript build fails with a module-not-found error, and so does loading the S5332 helper and its tests. That blocks the whole analysis package, not just this rule.

The fix is to publish a Commons release that includes identifier-urls.json and bump the dependency in package.json and package-lock.json in this PR. If that release isn't ready yet, keep the inline inventory until it is.

Was this helpful? React with 👍 / 👎

Comment on lines +22 to +24
const IDENTIFIER_URLS = new Map<string, RegExp>(
Object.entries(identifierUrls).map(([domain, pattern]) => [domain, new RegExp(pattern)]),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Cross-PR: Empty exported inventory makes every identifier URL fail to match · SonarSource/sonar-analyzer-commons #590

Breaks when SonarJS bumps @sonarsource/analyzer-commons-configurations to a release built from SonarSource/sonar-analyzer-commons#590 before that PR fills in the inventory.

How

  1. CleartextProtocolFilter.IDENTIFIER_URLS is Map.of() (CleartextProtocolFilter.java:228), and IdentifierUrlsExporter.toJson() serializes it, so identifier-urls.json comes out as {}.
  2. This PR now builds IDENTIFIER_URLS only from that JSON (identifier-urls.ts:22-24), which replaces the inline map it removed.
  3. With an empty map, IDENTIFIER_URLS.get(...) is always undefined and isKnownIdentifierUrl returns false for every URL (identifier-urls.ts:40).
  4. Every case in identifiers asserts true (identifier-urls.test.ts:113-115), so they all fail, and S5332 can no longer recognize any identifier URL.

Fix

  • This PR: Don't bump the dependency until a Commons release ships a populated identifier-urls.json. Or keep the inline inventory as the source until then.
  • SonarSource/sonar-analyzer-commons#590: Fill IDENTIFIER_URLS with the full authority-to-pattern inventory before releasing the npm artifact that SonarJS will consume.

Compared 827218c with SonarSource/sonar-analyzer-commons#590 at c0c18f9

Was this helpful? React with 👍 / 👎

@gitar-bot gitar-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ This PR is blocked due to unresolved code review findings.

Comment gitar unblock to override this block and allow merging.

Configure merge blocking · Maintainers can dismiss this review.

@datadog-sonarsource

datadog-sonarsource Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Pipelines

❌ Errors

Your PR has failed checks. Please review the issues below and take necessary action before merging.

🚦 4 Pipeline jobs failed

Build | Build SonarJS on Linux — 🔧 Needs a code fix, caused by this PR

View more details · View in GitHub Actions

Build | Knip — 🔧 Needs a code fix, caused by this PR

View more details · View in GitHub Actions

Build | Unit tests JavaScript on Windows — 🔧 Needs a code fix, caused by this PR

View more details · View in GitHub Actions

View all 4 failed jobs.

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 417a1c3 | Docs | View more details | Give us feedback!

Comment on lines +23 to +29
it('uses an empty identifier inventory', () => {
assert.deepEqual(identifierUrls, {});
});

it('does not match URLs when the inventory is empty', () => {
assert.equal(isKnownIdentifierUrl('http://identifier.invalid/schema'), false);
});

@gitar-bot gitar-bot Bot Nov 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Matcher parsing logic is no longer tested; empty-map test is vacuous

This commit removes the roughly 100 lines of matcher tests. The two tests left only check that the shipped inventory is {} and that one URL returns false. With an empty IDENTIFIER_URLS map, isKnownIdentifierUrl returns false for any input, so the second test passes no matter what the authority regex, port handling, case folding, or suffix logic in identifier-urls.ts:34-40 does. None of that parsing code is exercised. The deepEqual(identifierUrls, {}) test also fails as soon as a Commons bump fills in the inventory, and it locks in the current state where the S5332 identifier exception does nothing. A fix is to have the matcher take the inventory as input, e.g. createIdentifierUrlMatcher(inventory), and restore the parsing tests against fixture data, without asserting on the contents of the published JSON.

Build the matcher from an inventory you pass in, so tests can supply fixture data:

export function createIdentifierUrlMatcher(inventory: Record<string, string>) {
  const urls = new Map(Object.entries(inventory).map(([d, p]) => [d, new RegExp(p)]));
  return (value: string): boolean => {
    const match = /^http:\/\/([^\s/?#\]+)([/?#][^\s\]*)?$/i.exec(value);
    if (!match) return false;
    const remainder = match[2] ?? '';
    const suffix = remainder.startsWith('/') ? remainder : `/${remainder}`;
    return urls.get(match[1].toLowerCase())?.test(suffix) === true;
  };
}
export const isKnownIdentifierUrl = createIdentifierUrlMatcher(identifierUrls);

Was this helpful? React with 👍 / 👎

export function isKnownIdentifierUrl(value: string): boolean {
// Do not use URL.hostname (drops ports), URL.host (normalizes :80), or a lowercased
// URL (changes identifier spelling). Only the authority is case-insensitive.
const match = /^http:\/\/([^\s/?#\\]+)([/?#][^\s\\]*)?$/i.exec(value);

@gitar-bot gitar-bot Bot Nov 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Cross-PR: Whitespace-padded identifier URLs match in Java but not SonarJS · SonarSource/sonar-analyzer-commons #590

Breaks when sonar-analyzer-commons#590 merges and a later Commons release adds inventory entries, and SonarJS then checks an identifier URL literal that has leading or trailing whitespace.

How

  1. packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:34 runs the anchored regex ^http://...$ on the raw value, and [^\s...] rejects any surrounding whitespace.
  2. CleartextProtocolFilter.isKnownIdentifier in sonar-analyzer-commons#590 calls url.strip() before matching.
  3. sonar-analyzer-commons-configurations/src/main/packaging/npm/README.md:27 (sonar-analyzer-commons#590) tells consumers to strip surrounding whitespace first.
  4. As a result, for an input like ' http://<known>/x ', Java returns true and SonarJS returns false. That goes against the file's own comment, "keep the matching semantics aligned".

Fix

  • This PR: Trim surrounding whitespace before running the authority regex, e.g. .exec(value.trim()), and add a test for a padded URL.
  • sonar-analyzer-commons#590: Nothing needed.

Compared 417a1c3 with sonar-analyzer-commons#590 at c0c18f9

Strip surrounding whitespace before matching, like Java strip():

const match = /^http:\/\/([^\s/?#\]+)([/?#][^\s\]*)?$/i.exec(value.trim());

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Analyzing CI failures

Code Review 🚫 Blocked 1 closed / 6 findings

🟡 Medium risk · The new identifier URL matcher controls URL recognition; parsing or regex mismatches could misclassify candidate identifiers during rule analysis.

Adds an identifier URL inventory and matcher for S5332 that loads host-to-path-pattern data from Analyzer Commons JSON, but the PR is blocked. The imported identifier-urls.json is not in any published Commons package, so the build fails with a module-not-found error. Even once it resolves, an empty inventory would make every identifier URL fail to match.

  • Path prefixes lack a segment boundary, so unrelated paths such as http://hapifhir.io/fhir-proxy are wrongly treated as identifiers
  • Whitespace-padded identifier URLs match in Commons Java but not in SonarJS, because the regex runs on the untrimmed value
  • Matcher parsing logic is no longer exercised by tests, and the empty-map test passes regardless of the parsing code
🚨 Bug: Imported identifier-urls.json is not in any published Commons package

📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:18

This commit swaps the inline map for import identifierUrls from '@sonarsource/analyzer-commons-configurations/identifier-urls.json'. The root package.json still pins 2.31.0-5284, and that tarball contains only README.md, secret-patterns.json and secret-exclusion-corpus.json. I unpacked every published version (2.32.0-5319, 2.33.0-5369, 2.34.0-15495, 2.35.0-15509) and none of them ships identifier-urls.json. The files whitelist in the latest package.json also leaves it out. So the TypeScript build fails with a module-not-found error, and so does loading the S5332 helper and its tests. That blocks the whole analysis package, not just this rule.

The fix is to publish a Commons release that includes identifier-urls.json and bump the dependency in package.json and package-lock.json in this PR. If that release isn't ready yet, keep the inline inventory until it is.

⚠️ Cross-PR: Empty exported inventory makes every identifier URL fail to match · SonarSource/sonar-analyzer-commons #590

📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:22-24 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:40 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts:22-36

Breaks when SonarJS bumps @sonarsource/analyzer-commons-configurations to a release built from SonarSource/sonar-analyzer-commons#590 before that PR fills in the inventory.

How

  1. CleartextProtocolFilter.IDENTIFIER_URLS is Map.of() (CleartextProtocolFilter.java:228), and IdentifierUrlsExporter.toJson() serializes it, so identifier-urls.json comes out as {}.
  2. This PR now builds IDENTIFIER_URLS only from that JSON (identifier-urls.ts:22-24), which replaces the inline map it removed.
  3. With an empty map, IDENTIFIER_URLS.get(...) is always undefined and isKnownIdentifierUrl returns false for every URL (identifier-urls.ts:40).
  4. Every case in identifiers asserts true (identifier-urls.test.ts:113-115), so they all fail, and S5332 can no longer recognize any identifier URL.

Fix

  • This PR: Don't bump the dependency until a Commons release ships a populated identifier-urls.json. Or keep the inline inventory as the source until then.
  • SonarSource/sonar-analyzer-commons#590: Fill IDENTIFIER_URLS with the full authority-to-pattern inventory before releasing the npm artifact that SonarJS will consume.

Compared 827218c with SonarSource/sonar-analyzer-commons#590 at c0c18f9

💡 Edge Case: Path prefixes have no segment boundary, so unrelated paths match

📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:21-35 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:82

Every path regex is an open prefix such as /^\/fhir/, /^\/rels/, /^\/sct/ or /^\/schema/, with nothing checking where the path segment ends. As a result, http://hapifhir.io/fhir-proxy, http://cloud.gbtec.de/release-notes, http://snomed.info/sctapi and http://dbpedia.org/resources-api are all treated as identifiers, and their cleartext URLs would go unreported. The tests do check ? and # as boundaries (/GPX?version=1#section), which suggests segment-level matching is what's intended.

Fix: put a boundary after each prefix, e.g. (?=[/?#]|$). Alternatively, make the boundary part of the matcher so individual entries don't each need it.

Require that the matched prefix ends at a path, query or fragment boundary (or the end of the string), except for whole-host '/' entries.
const suffix = remainder.startsWith('/') ? remainder : `/${remainder}`;
const pattern = IDENTIFIER_URLS.get(match[1].toLowerCase());
if (!pattern) {
  return false;
}
const m = pattern.exec(suffix);
return m !== null && (m[0] === '/' || /^(?:[/?#]|$)/.test(suffix.slice(m[0].length)));
💡 Quality: Matcher parsing logic is no longer tested; empty-map test is vacuous

📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts:23-29 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:22-24 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:31-40

This commit removes the roughly 100 lines of matcher tests. The two tests left only check that the shipped inventory is {} and that one URL returns false. With an empty IDENTIFIER_URLS map, isKnownIdentifierUrl returns false for any input, so the second test passes no matter what the authority regex, port handling, case folding, or suffix logic in identifier-urls.ts:34-40 does. None of that parsing code is exercised. The deepEqual(identifierUrls, {}) test also fails as soon as a Commons bump fills in the inventory, and it locks in the current state where the S5332 identifier exception does nothing. A fix is to have the matcher take the inventory as input, e.g. createIdentifierUrlMatcher(inventory), and restore the parsing tests against fixture data, without asserting on the contents of the published JSON.

Build the matcher from an inventory you pass in, so tests can supply fixture data
export function createIdentifierUrlMatcher(inventory: Record<string, string>) {
  const urls = new Map(Object.entries(inventory).map(([d, p]) => [d, new RegExp(p)]));
  return (value: string): boolean => {
    const match = /^http:\/\/([^\s/?#\]+)([/?#][^\s\]*)?$/i.exec(value);
    if (!match) return false;
    const remainder = match[2] ?? '';
    const suffix = remainder.startsWith('/') ? remainder : `/${remainder}`;
    return urls.get(match[1].toLowerCase())?.test(suffix) === true;
  };
}
export const isKnownIdentifierUrl = createIdentifierUrlMatcher(identifierUrls);
💡 Cross-PR: Whitespace-padded identifier URLs match in Java but not SonarJS · SonarSource/sonar-analyzer-commons #590

📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:34

Breaks when sonar-analyzer-commons#590 merges and a later Commons release adds inventory entries, and SonarJS then checks an identifier URL literal that has leading or trailing whitespace.

How

  1. packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:34 runs the anchored regex ^http://...$ on the raw value, and [^\s...] rejects any surrounding whitespace.
  2. CleartextProtocolFilter.isKnownIdentifier in sonar-analyzer-commons#590 calls url.strip() before matching.
  3. sonar-analyzer-commons-configurations/src/main/packaging/npm/README.md:27 (sonar-analyzer-commons#590) tells consumers to strip surrounding whitespace first.
  4. As a result, for an input like ' http://<known>/x ', Java returns true and SonarJS returns false. That goes against the file's own comment, "keep the matching semantics aligned".

Fix

  • This PR: Trim surrounding whitespace before running the authority regex, e.g. .exec(value.trim()), and add a test for a padded URL.
  • sonar-analyzer-commons#590: Nothing needed.

Compared 417a1c3 with sonar-analyzer-commons#590 at c0c18f9

Strip surrounding whitespace before matching, like Java `strip()`
const match = /^http:\/\/([^\s/?#\]+)([/?#][^\s\]*)?$/i.exec(value.trim());
✅ 1 closed
✅ Quality: Inventory claims to mirror Analyzer Commons but differs from it

📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:18-20 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:38 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:76-82 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts:21 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts:45-46 📄 packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts:102
Line 18 (and the test header) say this inventory and its matching rules are kept in sync with Analyzer Commons. On master, CleartextProtocolFilter.SAFE_URL_PREFIXES has only two entries, http://jabber.org/protocol/ and http://apache.org/xml/features/, and isKnownIdentifierUrl lowercases the whole URL before a startsWith check.

This port differs in three ways:

  • apache.org /xml/features/ is missing, so http://apache.org/xml/features/validation/schema returns false here and true in Commons.
  • Jabber uses /^\/protocol/ with no trailing slash, so bare /protocol and things like /protocolfoo match here but not in Commons.
  • Paths are compared case-sensitively here and case-insensitively in Commons.

The tests are also not aligned with CleartextProtocolFilterTest. For example, http://www.topografix.com/gpx/1/1 is asserted as a non-identifier.

Fix one of two ways: add the Commons entries and align the semantics, or drop the 'in sync' claim until the Commons counterpart (JS-2698) actually lands.

🤖 Prompt for agents
Code Review: Adds an identifier URL inventory and matcher for S5332 that loads host-to-path-pattern data from Analyzer Commons JSON, but the PR is blocked. The imported `identifier-urls.json` is not in any published Commons package, so the build fails with a module-not-found error. Even once it resolves, an empty inventory would make every identifier URL fail to match.
  
  - Path prefixes lack a segment boundary, so unrelated paths such as `http://hapifhir.io/fhir-proxy` are wrongly treated as identifiers
  - Whitespace-padded identifier URLs match in Commons Java but not in SonarJS, because the regex runs on the untrimmed value
  - Matcher parsing logic is no longer exercised by tests, and the empty-map test passes regardless of the parsing code

1. 💡 Edge Case: Path prefixes have no segment boundary, so unrelated paths match
   Files: packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:21-35, packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:82

   Every path regex is an open prefix such as `/^\/fhir/`, `/^\/rels/`, `/^\/sct/` or `/^\/schema/`, with nothing checking where the path segment ends. As a result, `http://hapifhir.io/fhir-proxy`, `http://cloud.gbtec.de/release-notes`, `http://snomed.info/sctapi` and `http://dbpedia.org/resources-api` are all treated as identifiers, and their cleartext URLs would go unreported. The tests do check `?` and `#` as boundaries (`/GPX?version=1#section`), which suggests segment-level matching is what's intended.
   
   Fix: put a boundary after each prefix, e.g. `(?=[/?#]|$)`. Alternatively, make the boundary part of the matcher so individual entries don't each need it.

   Fix (Require that the matched prefix ends at a path, query or fragment boundary (or the end of the string), except for whole-host '/' entries.):
   const suffix = remainder.startsWith('/') ? remainder : `/${remainder}`;
   const pattern = IDENTIFIER_URLS.get(match[1].toLowerCase());
   if (!pattern) {
     return false;
   }
   const m = pattern.exec(suffix);
   return m !== null && (m[0] === '/' || /^(?:[/?#]|$)/.test(suffix.slice(m[0].length)));

2. 🚨 Bug: Imported identifier-urls.json is not in any published Commons package
   Files: packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:18

   This commit swaps the inline map for `import identifierUrls from '@sonarsource/analyzer-commons-configurations/identifier-urls.json'`. The root package.json still pins `2.31.0-5284`, and that tarball contains only `README.md`, `secret-patterns.json` and `secret-exclusion-corpus.json`. I unpacked every published version (2.32.0-5319, 2.33.0-5369, 2.34.0-15495, 2.35.0-15509) and none of them ships `identifier-urls.json`. The `files` whitelist in the latest package.json also leaves it out. So the TypeScript build fails with a module-not-found error, and so does loading the S5332 helper and its tests. That blocks the whole analysis package, not just this rule.
   
   The fix is to publish a Commons release that includes `identifier-urls.json` and bump the dependency in package.json and package-lock.json in this PR. If that release isn't ready yet, keep the inline inventory until it is.

3. ⚠️ Cross-PR: Empty exported inventory makes every identifier URL fail to match · [SonarSource/sonar-analyzer-commons #590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>)
   Files: packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:22-24, packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:40, packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts:22-36

   **Breaks when** SonarJS bumps `@sonarsource/analyzer-commons-configurations` to a release built from [SonarSource/sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>) before that PR fills in the inventory.
   
   **How**
   
   1. `CleartextProtocolFilter.IDENTIFIER_URLS` is `Map.of()` (CleartextProtocolFilter.java:228), and `IdentifierUrlsExporter.toJson()` serializes it, so `identifier-urls.json` comes out as `{}`.
   2. This PR now builds `IDENTIFIER_URLS` only from that JSON (identifier-urls.ts:22-24), which replaces the inline map it removed.
   3. With an empty map, `IDENTIFIER_URLS.get(...)` is always `undefined` and `isKnownIdentifierUrl` returns `false` for every URL (identifier-urls.ts:40).
   4. Every case in `identifiers` asserts `true` (identifier-urls.test.ts:113-115), so they all fail, and S5332 can no longer recognize any identifier URL.
   
   **Fix**
   
   - **This PR:** Don't bump the dependency until a Commons release ships a populated `identifier-urls.json`. Or keep the inline inventory as the source until then.
   - **[SonarSource/sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>):** Fill `IDENTIFIER_URLS` with the full authority-to-pattern inventory before releasing the npm artifact that SonarJS will consume.
   
   _Compared `827218c` with [SonarSource/sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>) at `c0c18f9`_

4. 💡 Quality: Matcher parsing logic is no longer tested; empty-map test is vacuous
   Files: packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts:23-29, packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:22-24, packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:31-40

   This commit removes the roughly 100 lines of matcher tests. The two tests left only check that the shipped inventory is `{}` and that one URL returns `false`. With an empty `IDENTIFIER_URLS` map, `isKnownIdentifierUrl` returns `false` for any input, so the second test passes no matter what the authority regex, port handling, case folding, or `suffix` logic in `identifier-urls.ts:34-40` does. None of that parsing code is exercised. The `deepEqual(identifierUrls, {})` test also fails as soon as a Commons bump fills in the inventory, and it locks in the current state where the S5332 identifier exception does nothing. A fix is to have the matcher take the inventory as input, e.g. `createIdentifierUrlMatcher(inventory)`, and restore the parsing tests against fixture data, without asserting on the contents of the published JSON.

   Fix (Build the matcher from an inventory you pass in, so tests can supply fixture data):
   export function createIdentifierUrlMatcher(inventory: Record<string, string>) {
     const urls = new Map(Object.entries(inventory).map(([d, p]) => [d, new RegExp(p)]));
     return (value: string): boolean => {
       const match = /^http:\/\/([^\s/?#\]+)([/?#][^\s\]*)?$/i.exec(value);
       if (!match) return false;
       const remainder = match[2] ?? '';
       const suffix = remainder.startsWith('/') ? remainder : `/${remainder}`;
       return urls.get(match[1].toLowerCase())?.test(suffix) === true;
     };
   }
   export const isKnownIdentifierUrl = createIdentifierUrlMatcher(identifierUrls);

5. 💡 Cross-PR: Whitespace-padded identifier URLs match in Java but not SonarJS · [SonarSource/sonar-analyzer-commons #590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>)
   Files: packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:34

   **Breaks when** [sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>) merges and a later Commons release adds inventory entries, and SonarJS then checks an identifier URL literal that has leading or trailing whitespace.
   
   **How**
   
   1. `packages/analysis/src/jsts/rules/S5332/identifier-urls.ts:34` runs the anchored regex `^http://...$` on the raw `value`, and `[^\s...]` rejects any surrounding whitespace.
   2. `CleartextProtocolFilter.isKnownIdentifier` in [sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>) calls `url.strip()` before matching.
   3. `sonar-analyzer-commons-configurations/src/main/packaging/npm/README.md:27` ([sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>)) tells consumers to strip surrounding whitespace first.
   4. As a result, for an input like `' http://<known>/x '`, Java returns true and SonarJS returns false. That goes against the file's own comment, "keep the matching semantics aligned".
   
   **Fix**
   
   - **This PR:** Trim surrounding whitespace before running the authority regex, e.g. `.exec(value.trim())`, and add a test for a padded URL.
   - **[sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>):** Nothing needed.
   
   _Compared `417a1c3` with [sonar-analyzer-commons#590](<https://github.com/SonarSource/sonar-analyzer-commons/pull/590>) at `c0c18f9`_

   Fix (Strip surrounding whitespace before matching, like Java `strip()`):
   const match = /^http:\/\/([^\s/?#\]+)([/?#][^\s\]*)?$/i.exec(value.trim());

Verification Rules 1/1 met PRs 1 inspected

Functional validation No results

Rules 1 passed 2 not applicable

✅ Repository license compliance

Broader impact PRs 1 inspected
Cross-PR 1 PR inspected 2 open impacts

PR 590: ACOMMONS-237 Export the S5332 identifier URL inventory through the shared npm package Inspected

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing compact verification results.
Unblock → Override a blocking verdict and allow merging.

Comment with these commands to change the behavior for this request:

Auto-apply Compact Unblock
gitar auto-apply:on         
gitar display:verbose         
gitar unblock         

Was this helpful? React with 👍 / 👎 | Gitar

@ZiadMarey ZiadMarey closed this Nov 4, 2026
@ZiadMarey
ZiadMarey deleted the zm/JS-2774 branch November 4, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants