Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/analysis/src/jsts/rules/S5332/identifier-urls.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import identifierUrls from '@sonarsource/analyzer-commons-configurations/identifier-urls.json' with { type: 'json' };
import { isKnownIdentifierUrl } from './identifier-urls.js';

describe('S5332 identifier URL matcher', () => {
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);
});
Comment on lines +23 to +29

@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 👍 / 👎

});
41 changes: 41 additions & 0 deletions packages/analysis/src/jsts/rules/S5332/identifier-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* 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 👍 / 👎


// The inventory is maintained in Analyzer Commons; keep the matching semantics aligned.
// A match identifies a possible identifier; the caller must also check its use in the AST.
const IDENTIFIER_URLS = new Map<string, RegExp>(
Object.entries<string>(identifierUrls).map(([domain, pattern]) => [domain, new RegExp(pattern)]),
);
Comment on lines +22 to +24

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 👍 / 👎


/**
* The Commons npm package shares the inventory as JSON. Its Java matcher,
* CleartextProtocolFilter.isKnownIdentifier(), is not exposed through that package.
* SonarJS therefore performs URL parsing and regex matching here in Node.js using the shared data.
*/
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 👍 / 👎

if (!match) {
return false;
}
const remainder = match[2] ?? '';
const suffix = remainder.startsWith('/') ? remainder : `/${remainder}`;
return IDENTIFIER_URLS.get(match[1].toLowerCase())?.test(suffix) === true;
}
Loading