mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
52d1a0720f
Sonar's S2871 flags a bare .sort(), which orders by UTF-16 code unit rather than by value. All 30 remaining sites turned out to sort ASCII strings — ISO dates, currency and country codes, setting keys, route names, OAuth scopes, zip entry names — where that order is already the intended one. Each says so explicitly now, with a comparator that reproduces it exactly rather than a localeCompare that would quietly reorder things. That mattered more than it sounds. Several of these feed something other than a display: useAtlas joins the sorted codes into the signature that decides whether to rebuild the region layer, daysSlice re-pins sorted dates positionally onto reordered days, and three timeline views pick a day's colour by indexOf into the sorted array. Switching any of them to a locale collator would have been a silent regression. Two smaller ones came with it. The .map(placeBtn) calls in FileManagerAssignModal pass the index as a second argument, which placeBtn declares and uses for its React key — correct already, now written out as .map((p, i) => placeBtn(p, i)). And budgetSlice's optimistic paid-toggle stored the raw boolean behind `paid as unknown as number`, while the schema declares z.number() and both the REST response and the WebSocket event carry 0/1. So the optimistic state and the confirmed state disagreed on type until a refresh. It normalises now, and the two tests that asserted `true` assert the contract. Four `| 0` truncations are listed in sonar-project.properties instead of changed. Two are a djb2 string hash where the 32-bit wrap is the algorithm. The other two look like plain truncation of a row id, but `| 0` is also coercing undefined to 0 there — the settlement flows the API returns carry no user_id, so that is the normal path, and Math.trunc returns NaN and crashes the lookup.
179 lines
10 KiB
Properties
179 lines
10 KiB
Properties
sonar.projectKey=TREK
|
|
sonar.projectName=TREK
|
|
sonar.sourceEncoding=UTF-8
|
|
|
|
# Sources — one entry per workspace package (plugin-sdk is standalone but lives in-repo)
|
|
sonar.sources=client/src,server/src,shared/src,plugin-sdk/src
|
|
|
|
# Tests — dedicated test dirs per package, plus co-located client tests in src
|
|
# and co-located shared schema/sanitize/i18n specs in shared/src
|
|
sonar.tests=server/tests,client/tests,client/e2e,client/src,shared/src,plugin-sdk/test
|
|
sonar.test.inclusions=\
|
|
server/tests/**/*.test.ts,\
|
|
client/tests/**/*.test.ts,\
|
|
client/tests/**/*.test.tsx,\
|
|
client/e2e/**/*.spec.ts,\
|
|
client/src/**/*.test.ts,\
|
|
client/src/**/*.test.tsx,\
|
|
shared/src/**/*.spec.ts,\
|
|
plugin-sdk/test/**/*.test.ts
|
|
|
|
# Keep generated output and test files out of source analysis
|
|
sonar.exclusions=\
|
|
**/node_modules/**,\
|
|
**/dist/**,\
|
|
**/build/**,\
|
|
**/coverage/**,\
|
|
client/dev-dist/**,\
|
|
**/*.test.ts,\
|
|
**/*.test.tsx,\
|
|
**/*.spec.ts,\
|
|
**/*.d.ts
|
|
|
|
# Coverage — lcov paths relative to repo root, one per package
|
|
# (each package's vitest config emits lcov to <pkg>/coverage/).
|
|
sonar.javascript.lcov.reportPaths=\
|
|
server/coverage/lcov.info,\
|
|
client/coverage/lcov.info,\
|
|
shared/coverage/lcov.info,\
|
|
plugin-sdk/coverage/lcov.info
|
|
|
|
# Infrastructure/bootstrap files plus generated/data files the vitest configs
|
|
# exclude from instrumentation (a file absent from lcov would otherwise count
|
|
# as 0% covered here — keep these in sync with each package's vitest.config.ts)
|
|
sonar.coverage.exclusions=\
|
|
server/src/index.ts,\
|
|
server/src/config.ts,\
|
|
server/src/db/database.ts,\
|
|
server/src/db/migrations.ts,\
|
|
server/src/db/seeds.ts,\
|
|
server/src/demo/**,\
|
|
server/src/nest/plugins/runtime/plugin-host-entry.ts,\
|
|
client/src/main.tsx,\
|
|
client/src/types.ts,\
|
|
shared/src/plugin-permissions.ts,\
|
|
shared/src/i18n/*/**,\
|
|
plugin-sdk/src/generated/**,\
|
|
plugin-sdk/src/lucide-icon-names.ts
|
|
|
|
# Locale files are large near-identical literal objects — keep them out of duplication metrics
|
|
sonar.cpd.exclusions=shared/src/i18n/**
|
|
|
|
# Locale files are UI copy — strings like "Password", "API key", "token" are labels,
|
|
# not credentials, and the secret detectors false-positive on them constantly.
|
|
# Ignore the secrets analyzer and the hard-coded-credential rules on i18n only.
|
|
sonar.issue.ignore.multicriteria=i18nsec1,i18nsec2,i18nsec3,rand1,rand2,rand3,rand4,rand5,rand6,cli1,hash1,hash2,hash3,hash4,hash5,hash6,hash7,hash8,ns1,ns2,frame1
|
|
sonar.issue.ignore.multicriteria.i18nsec1.ruleKey=secrets:*
|
|
sonar.issue.ignore.multicriteria.i18nsec1.resourceKey=shared/src/i18n/**
|
|
sonar.issue.ignore.multicriteria.i18nsec2.ruleKey=typescript:S2068
|
|
sonar.issue.ignore.multicriteria.i18nsec2.resourceKey=shared/src/i18n/**
|
|
sonar.issue.ignore.multicriteria.i18nsec3.ruleKey=typescript:S6418
|
|
sonar.issue.ignore.multicriteria.i18nsec3.resourceKey=shared/src/i18n/**
|
|
|
|
# S2245 (Math.random in a security context) — scoped per file, never globally, so
|
|
# a future Math.random() on an auth, token or crypto path still fails the gate.
|
|
# The two places where the value DID reach something that matters — the two
|
|
# idempotency-key producers — were fixed instead of listed here; they now share
|
|
# client/src/utils/randomId.ts.
|
|
#
|
|
# Studio + studioStore: element and spread ids inside a photo-book document. The
|
|
# whole document is stored and fetched as one blob scoped by journey id, so an
|
|
# element id is never a URL, a lookup key or an authorization subject. Worst case
|
|
# of a collision is documented in studioStore itself: two elements would share a
|
|
# selection.
|
|
sonar.issue.ignore.multicriteria.rand1.ruleKey=typescript:S2245
|
|
sonar.issue.ignore.multicriteria.rand1.resourceKey=client/src/components/Studio/**
|
|
sonar.issue.ignore.multicriteria.rand2.ruleKey=typescript:S2245
|
|
sonar.issue.ignore.multicriteria.rand2.resourceKey=client/src/store/studioStore.ts
|
|
# LoginWorld: canvas animation parameters — arc endpoints, flight speed, spawn
|
|
# delays. Nothing crosses a trust boundary; nothing leaves the browser.
|
|
sonar.issue.ignore.multicriteria.rand3.ruleKey=typescript:S2245
|
|
sonar.issue.ignore.multicriteria.rand3.resourceKey=client/src/pages/login/LoginWorld.tsx
|
|
# placesSession: a Google Places session token. It is a BILLING correlator, not a
|
|
# credential — Google groups requests by (API key, token) for pricing. Suggestions
|
|
# derive from the typed input, never from the token, so a guessed value cannot
|
|
# pull another user's results. The Math.random rung is also the third fallback,
|
|
# reached only where Web Crypto is absent entirely.
|
|
sonar.issue.ignore.multicriteria.rand4.ruleKey=typescript:S2245
|
|
sonar.issue.ignore.multicriteria.rand4.resourceKey=client/src/utils/placesSession.ts
|
|
# Cost-split rows: React list keys for ephemeral ticket rows, dropped before the
|
|
# payload is serialised (see writeTicketItems) so they never reach the API or DB.
|
|
sonar.issue.ignore.multicriteria.rand5.ruleKey=typescript:S2245
|
|
sonar.issue.ignore.multicriteria.rand5.resourceKey=client/src/components/Budget/CostsPanel.tsx
|
|
sonar.issue.ignore.multicriteria.cli1.ruleKey=typescript:S2245
|
|
sonar.issue.ignore.multicriteria.cli1.resourceKey=client/src/mobile/screens/trip/sheets/MCostSheet.tsx
|
|
|
|
# S4790 (weak hash) — every one of these derives a cache key or a filename, never
|
|
# a token, signature or integrity check, and the digest never reaches a client:
|
|
# the public URLs carry the plain placeId/photoId. Every genuine security hash in
|
|
# the server is already sha256 (backup codes, tokens, PKCE, the plugin audit
|
|
# chain). Swapping these would only orphan existing cached files — thumbnail.ts
|
|
# says so in its own comment.
|
|
sonar.issue.ignore.multicriteria.hash1.ruleKey=typescript:S4790
|
|
sonar.issue.ignore.multicriteria.hash1.resourceKey=server/src/nest/memories/**
|
|
sonar.issue.ignore.multicriteria.hash2.ruleKey=typescript:S4790
|
|
sonar.issue.ignore.multicriteria.hash2.resourceKey=server/src/nest/place-photos/**
|
|
sonar.issue.ignore.multicriteria.hash3.ruleKey=typescript:S4790
|
|
sonar.issue.ignore.multicriteria.hash3.resourceKey=server/src/nest/place-enrichment/**
|
|
|
|
# S4036 (OS command via PATH) — the plugin SDK is a CLI a plugin author runs on
|
|
# their own machine, and reaching git/npm through the developer's own PATH is the
|
|
# point of it. Every invocation passes an argv array, so nothing is shell-parsed.
|
|
# The one server-side site was NOT suppressed: it now resolves its binary to an
|
|
# absolute path (see kitinerary-extractor.service.ts).
|
|
sonar.issue.ignore.multicriteria.hash4.ruleKey=typescript:S4036
|
|
sonar.issue.ignore.multicriteria.hash4.resourceKey=plugin-sdk/src/cli/**
|
|
|
|
# S5332 (clear-text protocol) — neither site opens a connection.
|
|
# gpx-export: the GPX and XSI namespace URIs plus the schemaLocation pair. A
|
|
# namespace is an exact identifier fixed by the schema, so rewriting it to
|
|
# https:// would produce GPX that other tools reject.
|
|
sonar.issue.ignore.multicriteria.ns1.ruleKey=typescript:S5332
|
|
sonar.issue.ignore.multicriteria.ns1.resourceKey=server/src/nest/places/gpx-export.helpers.ts
|
|
# host-surface: 'http://x.invalid' is a throwaway base handed to new URL() so a
|
|
# relative path can be parsed — the very next line rejects the value if it
|
|
# resolved anywhere else. The string is part of the guard, not a destination, and
|
|
# .invalid is reserved by RFC 2606 precisely so it can never resolve.
|
|
sonar.issue.ignore.multicriteria.ns2.ruleKey=typescript:S5332
|
|
sonar.issue.ignore.multicriteria.ns2.resourceKey=server/src/nest/plugins/host/rpc/host-surface.rpc.ts
|
|
|
|
# S2819 (postMessage target origin) — the plugin frame is embedded with
|
|
# sandbox="allow-scripts allow-forms" and deliberately WITHOUT allow-same-origin,
|
|
# so the document inside has an opaque origin. There is no origin string that
|
|
# would match it: '*' is the only value that works, which makes the rule
|
|
# unsatisfiable here rather than unsatisfied. The receiving side authenticates on
|
|
# event.source instead.
|
|
sonar.issue.ignore.multicriteria.frame1.ruleKey=typescript:S2819
|
|
sonar.issue.ignore.multicriteria.frame1.resourceKey=client/src/components/Plugins/PluginFrame.tsx
|
|
# randomId is the module that MINTS idempotency keys, and its first two rungs are
|
|
# crypto.randomUUID and crypto.getRandomValues. The Math.random line is the third,
|
|
# reached only by a runtime with no Web Crypto at all, and the value it produces is
|
|
# a deduplication key the server scopes per (user, method, path) — not a token, not
|
|
# a secret. Replacing it is not possible: there is no stronger primitive left at
|
|
# that point, and deleting the rung would leave such a runtime with no key.
|
|
sonar.issue.ignore.multicriteria.rand6.ruleKey=typescript:S2245
|
|
sonar.issue.ignore.multicriteria.rand6.resourceKey=client/src/utils/randomId.ts
|
|
|
|
# S7767 (bitwise truncation) — Math.trunc is NOT equivalent to `| 0`, and all four
|
|
# sites depend on a difference the rule does not model.
|
|
#
|
|
# packingListPanel.helpers and todoListModel hold the same djb2-variant string
|
|
# hash, `h = ((h << 5) - h + chr) | 0`, whose only job is to stay bounded while it
|
|
# folds a category name into a colour index. Math.trunc would let h grow past 2^53,
|
|
# lose precision, and hand out different colours for the same names.
|
|
#
|
|
# BudgetPanel.helpers and TravelerPicker look like plain truncation of a user row
|
|
# id, but `| 0` is also coercing there: it turns undefined and NaN into 0, while
|
|
# Math.trunc returns NaN and the array lookup then yields undefined. That is not
|
|
# theoretical — the settlement `flows` the API returns carry a username and an
|
|
# avatar but no user_id, so colorForUserId(undefined) is the normal path for that
|
|
# section. Swapping it crashed two BudgetPanel tests on `.gradient` of undefined.
|
|
sonar.issue.ignore.multicriteria.hash5.ruleKey=typescript:S7767
|
|
sonar.issue.ignore.multicriteria.hash5.resourceKey=client/src/components/Packing/packingListPanel.helpers.ts
|
|
sonar.issue.ignore.multicriteria.hash6.ruleKey=typescript:S7767
|
|
sonar.issue.ignore.multicriteria.hash6.resourceKey=client/src/components/Todo/todoListModel.ts
|
|
sonar.issue.ignore.multicriteria.hash7.ruleKey=typescript:S7767
|
|
sonar.issue.ignore.multicriteria.hash7.resourceKey=client/src/components/Budget/BudgetPanel.helpers.ts
|
|
sonar.issue.ignore.multicriteria.hash8.ruleKey=typescript:S7767
|
|
sonar.issue.ignore.multicriteria.hash8.resourceKey=client/src/components/Planner/TravelerPicker.tsx
|