Skip to content

refactor(all-services): resolve sonarcloud reliability and security issues - #2604

Merged
yeshamavani merged 2 commits into
masterfrom
GH-2603
Sep 4, 2026
Merged

refactor(all-services): resolve sonarcloud reliability and security issues#2604
yeshamavani merged 2 commits into
masterfrom
GH-2603

Conversation

@piyushsinghgaur1

@piyushsinghgaur1 piyushsinghgaur1 commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes the 47 open/confirmed SonarCloud issues on the current leak period for
sourcefuse_loopback4-microservice-catalog. Each issue was fixed at the exact file and line
reported. No unrelated code was touched.

The bulk of this is mechanical and semantics-preserving. Two changes that looked mechanical turned
out to alter behaviour and were caught and corrected during review — both are called out below,
because neither was detectable by build / lint / test.


What changed

A. node: import protocol — 24 issues, 22 files
Core-module specifiers switched to the node: form (cryptonode:crypto, pathnode:path,
streamnode:stream, fsnode:fs, import('http')import('node:http')), preserving the
existing import style on each line (default / named / * as / require / type-only import()).

B. Modern JS built-ins — 6 issues

  • parseIntNumber.parseInt (packages/file-utils/src/constant.ts)
  • Error(...)new Error(...) (file-metadata.service.ts)
  • results.find(r => r)results.find(Boolean) (multer-storage.provider.ts)
  • Array(n)new Array(n) (idp-login.service.ts)
  • isNaN(...)Number.isNaN(Number(...)) (chat-session.service.ts) — see the section below
  • arr.indexOf(x) === -1!arr.includes(x) (custom-sf-changelog/writer-opts.js)

C. String.raw for backslash escapes — 2 issues
services/audit-service/src/utils/construct-where.ts lines 76 and 84. The resulting string is
byte-identical (verified) — it is passed to the Postgres connector as a regexp operand, so any
drift would have silently changed audit-log filtering.

D. CI / dependency security hotspots — 15 issues

  • docs.yml: pip install --only-binary :all: with pinned versions (mkdocs-material==9.7.7,
    mkdocs-include-markdown-plugin==7.3.0). The previous pip install ... --ignore-scripts was
    not a valid pip option — that flag is npm-only, and pip exits 2 on it. Verified against
    pip 26.0.1: zero occurrences of ignore-scripts in its source; the only --ignore* options are
    --ignore-installed and --ignore-requires-python.
  • release.yml: lerna pinned to 9.0.7 (matching root package.json + lockfile) in all three
    places, plus npx --ignore-scripts.
  • CLI scaffold template: npx --ignore-scripts lerna@9.0.7, and package.json.tpl bumped
    ^7.3.0^9.0.7 so the pin and the declared dependency agree.
  • Sandbox Dockerfiles: npm installnpm ci --ignore-scripts, with lockfiles added (below).

⚠️ Why Number(...) was added in chat-session.service.ts

This is the one change in this PR that deserves a careful read. Sonar asked for
isNaNNumber.isNaN, but the naive swap is not equivalent and would have silently disabled a
REST validation.

// before
if (isNaN(expireTime.valueOf())) { throw new HttpErrors.BadRequest('Expire time is not in correct format.'); }

// naive "fix" — WRONG
if (Number.isNaN(expireTime.valueOf())) { ... }

// what shipped — correct
if (Number.isNaN(Number(expireTime.valueOf()))) { ... }

These are two different functions, not aliases.

What it asks For "abc"
isNaN(x) "is x not convertible to a valid number?" — coerces first Number("abc") is NaNtrue
Number.isNaN(x) "is x exactly the NaN value?" — never coerces "abc" is a string → false

Number.isNaN is effectively typeof x === 'number' && x !== x. Any string short-circuits to
false — even the string "NaN".

There is a second subtlety: .valueOf() does not produce a number here.
String.prototype.valueOf() returns the primitive string back ("abc".valueOf() === "abc"). Only
Date.prototype.valueOf() yields a number, which is why the line reads as though it were already
numeric.

And expireTime really is a string at runtime. The controller declares:

// src/controllers/video-chat-session.controller.ts:82
@requestBody()
sessionOptions: SessionOptions,   // plain TS interface — NOT a @model

SessionOptions is an interface (src/types.ts:170), so TypeScript emits Object as the
design:type and LoopBack generates a loose {type: 'object'} schema with no per-property
coercion
. JSON has no Date type, so the value arrives as a string — the declared expireTime?: Date
is a compile-time fiction.

Impact had the naive version shipped: a garbage expireTime string would have passed validation,
then passed moment().isAfter() (which returns false for unparseable input, so no "in past" error
either), and reached vonage.service.ts:106moment("abc").unix()NaN sent to the Vonage SDK.
A clean 400 would have become a provider-side error or 500.

Why Number(...) is the right fix and not redundant: global isNaN(x) is specified as "coerce
x to Number, then test for NaN" — it literally is Number.isNaN(Number(x)). Writing the
conversion explicitly restores exactly what the global was doing internally, while still satisfying
the Sonar rule (no bare global isNaN). If the value genuinely is a Date, Number() is a free
no-op.

Verified identical to the original across 21 input classes, 0 divergences: valid/past/invalid
Date; ISO full and date-only strings; garbage, numeric, float, whitespace, "NaN", "Infinity"
and hex strings; timestamps, 1, -1, Infinity; true; [1], [1,2]; {}; and an object with a
custom valueOf(). Falsy inputs never reach the line — the enclosing if (expireTime) guard is
unchanged.


Second regression caught: --ignore-scripts vs bcrypt

sandbox/auth-multitenant-example depends on bcrypt, a native addon whose binding is built by
an install script. Adding --ignore-scripts meant the image would build fine and then crash at
require() with Could not locate the bindings file. This repo already records that these packages
need scripts, in root package.json:

"lavamoat": { "allowScripts": { "bcrypt": true, "sqlite3": true } }

Fixed by rebuilding the one vetted native module:

RUN npm ci --ignore-scripts && npm rebuild bcrypt && npm run build

This adds no new build requirement — the original npm install ran bcrypt's install script too.
workflow-ms-example has no native deps, so it needs no rebuild step; oauth-example still uses
plain npm ci (it was not in the 47-issue list), so its scripts still run.

Neither regression was catchable by the pipelines: every existing test passes a real Date object
(so both isNaN variants agree), and no CI job builds any Dockerfile.


Lockfiles for npm ci

npm ci fails without a package-lock.json, and these sandbox directories had none — they are npm
workspaces, so their deps only ever resolved into the root lockfile. Standalone lockfiles were
generated for every directory that needs one:

Directory Reason Entries
sandbox/auth-multitenant-example/ npm ci introduced here 1117
sandbox/workflow-ms-example/ npm ci introduced here 978
sandbox/oauth-example/ pre-existing npm ci that never had a lockfile 1088

Generated in an isolated temp directory (so npm could not walk up and rewrite the root lockfile),
via npm install --package-lock-only --ignore-scripts --no-audit. All three declare only registry
ranges — no file:/link:/workspace: deps — so standalone resolution is valid. Verified: root
lockfile SHA-256 unchanged by the generation; root npm install --package-lock-only re-resolve is
byte-identical, proving npm ignores nested workspace lockfiles.

Note: npm run clean-deps will not maintain these — it runs lerna exec --no-private, and all
three examples are "private": true. Refreshing them is a manual step.


Other intentional behaviour changes (CI/Docker only, no runtime code)

  • lerna@latestlerna@9.0.7 in release.yml. latest currently resolves to 10.0.1, a
    major ahead of what the repo declares. Practical impact is near-zero: npx already preferred the
    local 9.0.7.
  • Scaffold template moved lerna 7 → 9.0.7. Checked before bumping: the scaffold only uses
    lerna run / changed / version / clean (all still present in v9), there is no
    lerna bootstrap
    anywhere in the templates (removed in v8 — would have been the breakage), and
    the generated workflow already runs Node 24. Affects newly scaffolded projects only.
  • docs.yml pip step now actually runs instead of exiting 2. That workflow is currently
    disabled_manually on GitHub with 0 recorded runs, so this was a latent failure, not an outage.

Verification

Check Result
npm run build --workspaces --if-present ✅ exit 0
npm run lint --workspaces --if-present ✅ exit 0
npm run test --workspaces --if-present ✅ exit 0 — 1155 passing, 0 failing
pre-commit hook (lerna run test && lerna run lint) ✅ 26 test projects, 23 lint projects, 0 failing
Prettier on all changed .ts/.js ✅ clean
YAML parse of all 3 changed workflows ✅ clean
pip download --only-binary :all: on pinned versions ✅ full transitive tree resolves as wheels
npx --ignore-scripts lerna@9.0.7 --version 9.0.7, resolved locally, no network fetch
npm ci --ignore-scripts --dry-run per new lockfile ✅ 1116 / 977 / 1087 packages resolve

…ssues

- use node: protocol for core module imports in packages, services and tests
- use Number.parseInt, Number.isNaN, new Error and new Array over global forms
- use Boolean predicate in Array.find and Array.includes over Array.indexOf
- use String.raw for regex literals in audit-service construct-where
- pin pip versions and add --only-binary :all: in docs workflow
- pin lerna to 9.0.7 in release workflow and scaffold template
- use npm ci --ignore-scripts in sandbox Dockerfiles and add their lockfiles
- rebuild bcrypt after ignore-scripts install in auth-multitenant example
- refresh root package-lock.json

GH-2603
@sonarqubecloud

sonarqubecloud Bot commented Sep 1, 2026

Copy link
Copy Markdown

@Sourav-kashyap
Sourav-kashyap marked this pull request as ready for review September 1, 2026 12:33
@rohit-sourcefuse
rohit-sourcefuse removed their request for review September 4, 2026 08:08
@yeshamavani
yeshamavani merged commit c3a9ed1 into master Sep 4, 2026
9 checks passed
@yeshamavani
yeshamavani deleted the GH-2603 branch September 4, 2026 08:49
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.

Fix 47 SonarCloud issues (node: imports, JS built-ins, String.raw, CI/dependency hotspots)

4 participants