SonarCloud Issue Triage Report¶
Date: 2026-03-12 Project: devgem_forma-3d-connect Branch analyzed: feature/scaling-preparations (PR #535) Total issues: 769 issues + 6 security hotspots + 19.5% code duplication Total effort: ~3,890 min (~65 hours estimated by SonarCloud)
Summary¶
| Severity | Count | Real Problems | False Positive | Won't Fix |
|---|---|---|---|---|
| BLOCKER | 13 | 1 | 12 | 0 |
| CRITICAL | 39 | 36 | 0 | 3 |
| MAJOR | 132 | 132 | 0 | 0 |
| MINOR | 585 | 490 | 15 | 80 |
| Total | 769 | 659 | 27 | 83 |
| Type | Count |
|---|---|
| CODE_SMELL | 748 |
| VULNERABILITY | 12 |
| BUG | 9 |
Recommended Actions¶
| Action | Issues | Effort |
|---|---|---|
| Suppress as false positive | 27 | ~30 min |
| Suppress as won't fix | 83 | ~60 min |
| Auto-fixable (lint --fix) | ~350 | ~2 hours |
| Manual refactoring | ~280 | ~20 hours |
| Real bugs to fix immediately | 9 | ~2 hours |
| Scope exclusion (sonar config) | 20 | ~10 min |
Configuration Fixes¶
Before addressing individual issues, update sonar-project.properties to exclude test infrastructure files that are incorrectly analyzed as production code:
# Add to sonar.exclusions:
**/test/mocks/**,\
**/test/setup.*
This removes ~15–20 issues from apps/web/src/test/mocks/ that are test mock factories, not production code.
BLOCKER Issues (13)¶
S2068 — Hard-coded credentials (12 issues) → FALSE POSITIVE¶
Files: All audit.service.ts files + change-password-modal.tsx + user-form-modal.tsx
SonarCloud flags any variable containing the word "password" as a potential hard-coded credential. In this codebase:
- Audit services use constants like
PASSWORD_CHANGED: 'password.changed'andPASSWORD_RESET: 'password.reset'— these are audit event type names, not credentials. - change-password-modal.tsx and user-form-modal.tsx use
passwordas a form field variable name for validation logic — no actual secrets.
Recommended fix: Add // NOSONAR comments on the flagged lines:
| File | Lines | Reason |
|---|---|---|
apps/gridflock-service/src/audit/audit.service.ts |
19, 20 | Audit event name constants |
apps/order-service/src/audit/audit.service.ts |
27, 28 | Audit event name constants |
apps/print-service/src/audit/audit.service.ts |
19, 20 | Audit event name constants |
apps/shipping-service/src/audit/audit.service.ts |
19, 20 | Audit event name constants |
apps/web/src/components/users/change-password-modal.tsx |
38, 40 | Form field validation |
apps/web/src/components/users/user-form-modal.tsx |
77, 79 | Form field validation |
S2699 — Test without assertions (1 issue) → WON'T FIX¶
File: apps/web/src/components/ui/__tests__/pagination.test.tsx:150
This is an intentional crash/smoke test that verifies the <Pagination> component doesn't throw when given undefined props. The test name is "should handle undefined or NaN values gracefully". Not having an explicit assertion is the point — the test passes if no error is thrown.
Recommended fix: Add // NOSONAR to the test function.
BUG Issues (9)¶
S2871 — Array sort without compare function (8 issues) → PROBLEM¶
.sort() without a compare function converts elements to strings and sorts lexicographically. This can produce unexpected results for numbers and locale-sensitive strings.
| File | Line | Context | Evaluation |
|---|---|---|---|
apps/gateway/src/auth/services/permission.service.ts |
35 | .sort() on permission name strings |
Won't Fix — string array, lexicographic sort is fine for permission names used for caching/comparison |
apps/gridflock-service/src/auth/services/permission.service.ts |
38 | Same pattern | Won't Fix |
apps/order-service/src/auth/services/permission.service.ts |
38 | Same pattern | Won't Fix |
apps/print-service/src/auth/services/permission.service.ts |
38 | Same pattern | Won't Fix |
apps/order-service/src/shopify/shopify-token.service.ts |
283 | Object.keys(params).sort() for HMAC verification |
Won't Fix — ASCII key sorting for cryptographic signature verification, lexicographic is correct |
apps/order-service/src/storefront/guards/shopify-app-proxy.guard.ts |
113 | Same HMAC pattern | Won't Fix |
apps/web/src/pages/settings/index.tsx |
549 | Unknown array sort | Review — needs context check |
apps/shipping-service/src/auth/services/permission.service.ts |
38 | Same permission pattern | Won't Fix |
Note: 7 of 8 are actually fine — SonarCloud flags
.sort()on string arrays but lexicographic sorting is correct for these use cases (permission names, HMAC param keys). Add// NOSONARwith explanation.
S3923 — Identical conditional branches (1 issue) → PROBLEM (real bug)¶
File: apps/order-service/src/audit/audit.controller.ts:59
actorUserId: actorEmail ? undefined : undefined,
Both branches return undefined — this conditional is useless. The comment says "We'll filter by email below" suggesting this was a placeholder that was never completed. This should be cleaned up.
Recommended fix: Remove the conditional, just use actorUserId: undefined.
VULNERABILITY Issues (12)¶
All 12 are the same S2068 (hard-coded credentials) already covered in BLOCKER above. They are all false positives — audit event name constants and form field variable names.
CRITICAL Issues (39)¶
S3776 — Cognitive complexity too high (28 issues) → PROBLEM (refactor over time)¶
Functions exceeding the cognitive complexity threshold (default: 15). These are real code quality issues but not urgent bugs.
Top offenders:
| File | Line | Likely Cause |
|---|---|---|
apps/gateway/src/swagger/swagger-aggregator.service.ts |
109 | Complex Swagger merge logic |
apps/gridflock-service/src/gridflock/gridflock-pipeline.service.ts |
73 | Complex generation pipeline |
apps/order-service/src/orchestration/orchestration.service.ts |
105 | Order state machine |
apps/order-service/src/shopify/shopify-webhook.service.ts |
44 | Webhook event routing |
apps/order-service/src/shopify/shopify-backfill.service.ts |
217, 307 | Batch processing loops |
apps/print-service/src/simplyprint/simplyprint-api.client.ts |
69 | API client with many methods |
apps/web/src/pages/settings/index.tsx |
72 | Large settings page component |
apps/web/src/pages/mappings/new.tsx |
113 | Complex form wizard |
Recommendation: Track as tech debt. Refactor the worst offenders (orchestration service, settings page) when touching those files.
S2871 — Array sort (8 issues)¶
Already covered in BUG section above. ⅞ are won't fix, 1 needs review.
S3735 — Void used as value (3 issues) → WON'T FIX¶
| File | Line | Context |
|---|---|---|
apps/order-service/src/shipments/shipments.service.ts |
87 | void _shippingMethodId; — intentional unused parameter suppression |
apps/shipping-service/src/shipments/shipments.service.ts |
87 | Same pattern |
apps/web/src/sw.ts |
160 | Service worker lifecycle |
Recommended fix: Suppress with // NOSONAR — void expr is a standard TypeScript pattern for acknowledging unused parameters.
MAJOR Issues (132) — All PROBLEM, sorted by auto-fixability¶
Auto-fixable (79 issues)¶
These can likely be fixed with ESLint auto-fix rules or bulk find-and-replace:
| Rule | Count | Description | Fix |
|---|---|---|---|
| S6853 | 19 | Unneeded JSX curly braces {"text"} → "text" |
ESLint react/jsx-curly-brace-presence |
| S6582 | 14 | Prefer optional chaining a && a.b → a?.b |
@typescript-eslint/prefer-optional-chaining |
| S6819 | 6 | Self-closing JSX <Foo></Foo> → <Foo /> |
ESLint react/self-closing-comp |
| S7785 | 5 | Prefer for-of loop | @typescript-eslint/prefer-for-of |
Manual refactoring needed (53 issues)¶
| Rule | Count | Description | Priority |
|---|---|---|---|
| S3358 | 57 | Nested ternary operators | Medium — extract to variables or early returns |
| S2933 | 12 | Fields should be readonly |
Low — add readonly to constructor-injected fields |
| S4624 | 4 | Missing never return type on throw functions |
Low |
| S6481 | 4 | Constant objects in JSX (re-created each render) | Medium — move useMemo or extract constants |
| S6479 | 5 | Array index used as React key | Medium — use stable identifiers where available |
| S1854 | 2 | Dead stores in shopify-backfill.service.ts | Low — hasMore = false before break is redundant |
| S3923 | 1 | Identical conditional branches (real bug) | High |
| S2234 | 1 | Arguments possibly in wrong order (border-generator.ts:166) |
High — verify |
| S107 | 2 | Functions with too many parameters | Low |
S2234 — Arguments in wrong order (1 issue) → REVIEW CAREFULLY¶
File: libs/gridflock-core/src/lib/border-generator.ts:166
off = writeTriangle(buf, off, 0, 0, -1, cx, cy, z0, bx, by, z0, ax, ay, z0);
The function signature is writeTriangle(buf, offset, nx, ny, nz, ax, ay, az, bx, by, bz, cx, cy, cz). SonarCloud suspects ax, ay and bx, by may be swapped. For the bottom face, the reversed winding order (swapping A and B) is intentional to flip the normal direction. This is correct 3D geometry — reversed winding = inverted normal.
Evaluation: False positive — reversed winding order is intentional for the bottom face of a 3D mesh.
MINOR Issues (585) — Bulk Categories¶
Auto-fixable with ESLint rules (~350 issues)¶
| Rule | Count | Description | ESLint Rule |
|---|---|---|---|
| S7773 | 143 | Use ?? instead of \|\| |
@typescript-eslint/prefer-nullish-coalescing |
| S7735 | 46 | Use as type assertion syntax |
@typescript-eslint/consistent-type-assertions |
| S7764 | 38 | Unnecessary type parameters | @typescript-eslint/no-unnecessary-type-arguments |
| S7748 | 32 | Prefer ?. over && |
@typescript-eslint/prefer-optional-chaining |
| S4325 | 18 | Unnecessary type assertions | @typescript-eslint/no-unnecessary-type-assertion |
| S3863 | 18 | Use shorthand properties | object-shorthand |
| S7781 | 16 | Prefer destructuring | prefer-destructuring |
| S7778 | 9 | Use includes() over indexOf() |
@typescript-eslint/prefer-includes |
| S7758 | 4 | Dot notation over brackets | dot-notation |
| S7726 | 4 | Unnecessary type constraints | @typescript-eslint/no-unnecessary-type-constraint |
| S7757 | 2 | Consistent type imports | @typescript-eslint/consistent-type-imports |
| S7755 | 2 | Template literals | prefer-template |
| S6606 | 2 | Nullish coalescing | @typescript-eslint/prefer-nullish-coalescing |
| S7776 | 2 | startsWith/endsWith |
@typescript-eslint/prefer-string-starts-ends-with |
| S7780 | 1 | Return type annotation | @typescript-eslint/explicit-function-return-type |
Caution with S7773 (143 issues): Changing
||to??is NOT always safe.||treats"",0, andfalseas falsy, while??only treatsnull/undefined. The configuration files (configuration.ts) use|| 'default'patterns for env vars where empty string should also fall back — those should stay as||.
Won't Fix — Stylistic/Low-value (80 issues)¶
| Rule | Count | Description | Reason |
|---|---|---|---|
| S7763 | 48 | Redundant type aliases | Many from third-party library patterns (Sentry, Prisma). Changing would reduce readability. |
| S7772 | 47 | Sort union type constituents alphabetically | Pure cosmetic, no functional impact. Adds churn for zero value. |
Recommended: Suppress at project level in SonarCloud quality profile, or add // NOSONAR selectively.
Needs Manual Review (~55 issues)¶
| Rule | Count | Description | Notes |
|---|---|---|---|
| S6551 | 57 | Non-null assertion ! usage |
Many are in code that handles validated data (e.g., after null checks). Review case-by-case. |
| S6759 | 49 | JSX cognitive complexity | Large React components — refactor when touching. |
| S1874 | 37 | Deprecated API usage | Check which APIs and plan migration. |
| S4323 | 2 | Use type aliases for repeated union types | Minor improvement. |
| S7744 | 1 | Interface over type literal | Stylistic preference. |
| S6754 | 1 | React state mutation (theme-context.tsx:58) |
Likely false positive — uses useState correctly. |
False Positive — Test Mocks (~15 issues)¶
Files in apps/web/src/test/mocks/ are test infrastructure, not production code. Fix by adding to sonar.exclusions in sonar-project.properties.
Security Hotspots (6)¶
S5852 — Regex vulnerable to backtracking DoS (3 hotspots) → REVIEW¶
| File | Line | Regex |
|---|---|---|
apps/web/src/components/users/user-form-modal.tsx |
72 | Email validation: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ |
libs/utils/src/lib/string.ts |
47, 54 | String utility regexes |
Evaluation: The email regex is simple and bounded — no catastrophic backtracking risk in practice. However, for defense-in-depth, consider using a well-tested email validation library.
Recommended: Mark as reviewed/safe in SonarCloud.
S2245 — Pseudorandom number generator (2 hotspots) → WON'T FIX¶
| File | Line | Context |
|---|---|---|
apps/order-service/src/retry-queue/retry-queue.service.ts |
133 | Math.random() for retry jitter |
apps/shipping-service/src/retry-queue/retry-queue.service.ts |
133 | Same pattern |
Math.random() is used to add ±10% jitter to retry delays. This is a standard distributed systems pattern — no cryptographic security requirement.
Recommended: Mark as reviewed/safe in SonarCloud, or add // NOSONAR with comment explaining jitter usage.
S5443 — Publicly writable directory (1 hotspot) → WON'T FIX¶
File: apps/gridflock-service/src/gridflock/gridflock.processor.ts:68
Uses /tmp/gridflock-stl as default STL output path, overridable via STL_OUTPUT_PATH env var. In production, this is configured to a secure path. The /tmp fallback is for local development only.
Recommended: Mark as reviewed/safe.
Code Duplication (19.5% / 28,008 lines)¶
Project totals: 66,133 lines of code, 28,008 duplicated lines (33.4% overall, 19.5% on new code), 402 duplicated blocks across 243 files.
The duplication quality gate requires ≤ 3.0% — the project is at 19.5%. This is the single largest quality gate failure.
Root Cause: Cross-Service Code Duplication¶
Nearly all duplication comes from identical files copied across microservices. The architecture has 5 services (gateway, order-service, print-service, shipping-service, gridflock-service) that share common infrastructure code that was copy-pasted rather than extracted into shared libraries.
Duplication by Category¶
1. Observability Infrastructure (~3,100 dup lines) → Extract to libs/service-common¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/observability/services/business-observability.service.ts |
4 services | 1,832 | 98% |
src/observability/observability.controller.ts |
4 services | 804 | 93% |
These files are nearly identical across all backend services. The business-observability.service.ts alone accounts for 1,832 duplicated lines.
Recommendation: Extract into libs/service-common or a new libs/observability shared library. Each service imports and configures via dependency injection.
2. Authentication & Authorization (~1,100 dup lines) → Extract to libs/service-common¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/auth/services/auth.service.ts |
5 services | 884 | 96% |
src/auth/dto/login.dto.ts |
5 services | 210 | 91% |
Auth logic is duplicated across all 5 services including the gateway.
Recommendation: Extract shared auth service and DTOs into libs/service-common. Service-specific auth customizations can extend the base class.
3. Configuration (~1,300 dup lines) → Extract to libs/config¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/config/system-config.service.ts |
4 services | 752 | 94% |
src/config/env.validation.ts |
4 services | 536 | 91% |
Environment validation and system config management is identical across services.
Recommendation: Extract shared config patterns into libs/config. Each service keeps only its service-specific env vars.
4. Event Log (~1,050 dup lines) → Extract to libs/service-common¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/event-log/event-log.controller.ts |
4 services | 544 | 94% |
src/event-log/event-log.service.ts |
4 services | 504 | 94% |
Event logging is a cross-cutting concern duplicated across all backend services.
Recommendation: Extract into a shared NestJS dynamic module in libs/service-common.
5. Notifications (~1,100 dup lines) → Extract to libs/service-common¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/notifications/email.service.ts |
3 services | 606 | 96% |
src/notifications/notifications.service.ts |
2 services | 330 | 91% |
Email and notification services are duplicated across order, print, and shipping services.
Recommendation: Extract into a shared notifications module.
6. Product Mappings (~1,500 dup lines) → Extract to shared lib or merge¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/product-mappings/product-mappings.repository.ts |
2 services | 518 | 98% |
src/product-mappings/product-mappings.service.ts |
2 services | 414 | 93% |
src/product-mappings/dto/product-mapping.dto.ts |
2 services | 308 | 98% |
src/product-mappings/dto/part-library.dto.ts |
2 services | 84 | 91% |
Product mapping logic is duplicated between order-service and print-service.
Recommendation: Extract shared DTOs to libs/domain-contracts. Extract repository/service to a shared module, or designate one service as the single owner.
7. Shipments & SendCloud (~1,700 dup lines) → Consolidate ownership¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/shipments/shipments.service.ts |
2 services | 588 | 96% |
src/sendcloud/sendcloud.service.ts |
1 (internal) | 455 | 94% |
src/shipments/shipments.repository.ts |
2 services | 226 | 95% |
src/sendcloud/sendcloud-webhook.service.ts |
1 (internal) | 221 | 93% |
src/sendcloud/sendcloud-webhook.types.ts |
2 services | 222 | 92% |
Shipment and SendCloud integration code is duplicated between order-service and shipping-service.
Recommendation: Designate shipping-service as the single owner of shipment/SendCloud logic. Other services communicate via events.
8. Print Jobs (~1,070 dup lines) → Consolidate ownership¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/print-jobs/print-jobs.repository.ts |
1 (internal) | 286 | 98% |
src/print-jobs/stuck-job-monitor.service.ts |
2 services | 462 | 95% |
src/print-jobs/events/print-job.events.ts |
2 services | 406 | 97% |
src/print-jobs/dto/print-job.dto.ts |
2 services | 170 | 96% |
Print job management is duplicated between order-service and print-service.
Recommendation: Designate print-service as the single owner. Extract shared event types and DTOs to libs/domain-contracts.
9. Other Cross-Cutting Concerns (~1,000 dup lines)¶
| File Pattern | Services | Dup Lines (total) | Avg Density |
|---|---|---|---|
src/audit/audit.service.ts |
3 services | 342 | 92% |
src/retry-queue/retry-queue.service.ts |
2 services | 318 | 95% |
src/webhook-idempotency/webhook-idempotency.repository.ts |
2–3 services | 384 | 96% |
src/versioning/deprecated.decorator.ts |
4 services | 172 | 92% |
src/tenancy/tenant-context.service.ts |
2 services | 180 | 93% |
src/retry-queue/retry-queue.repository.ts |
2 services | 186 | 94% |
src/sendcloud/events/shipment.events.ts |
2 services | 252 | 94% |
src/simplyprint/dto/simplyprint-job.dto.ts |
2 services | 88 | 92% |
Recommendation: Extract all infrastructure patterns (audit, retry, webhook idempotency, versioning, tenancy) into libs/service-common. Extract shared DTOs/events to libs/domain-contracts.
Duplication Reduction Strategy¶
| Phase | Action | Estimated Dup Lines Removed | Target Library |
|---|---|---|---|
| D1 | Extract observability module | ~3,100 | libs/service-common |
| D2 | Extract auth module + DTOs | ~1,100 | libs/service-common |
| D3 | Extract config patterns | ~1,300 | libs/config |
| D4 | Extract event-log module | ~1,050 | libs/service-common |
| D5 | Extract notification module | ~1,100 | libs/service-common |
| D6 | Consolidate product mapping DTOs | ~1,500 | libs/domain-contracts |
| D7 | Consolidate shipment ownership | ~1,700 | shipping-service owns |
| D8 | Consolidate print job ownership | ~1,070 | print-service owns |
| D9 | Extract infrastructure patterns | ~1,000 | libs/service-common |
| Total recoverable | ~12,900 |
Removing ~12,900 of the 28,008 duplicated lines would bring the duplication rate from 33.4% to approximately 16%. Further reductions require domain-level architectural decisions about service boundaries.
Important: Duplication extraction is a significant architectural refactoring effort. Each phase should be treated as a separate PR with thorough testing. Phases D1–D5 and D9 are infrastructure-level changes with low domain risk. Phases D6–D8 involve domain ownership decisions that require architectural review.
Recommended Implementation Order¶
Phase 1: Quick Wins (30 min)¶
- Update
sonar-project.propertiesto exclude test mocks (~15 issues removed) - Add
// NOSONARto 12 false-positive S2068 credential warnings - Add
// NOSONARto the S2699 test assertion warning - Add
// NOSONARto 3 S3735 void-as-value warnings
Phase 2: Real Bug Fixes (1 hour)¶
- Fix S3923: Remove useless conditional in
audit.controller.ts:59 - Add
// NOSONARto 7 legitimate S2871.sort()calls with explanation - Verify S2234 argument order in
border-generator.ts:166(likely correct)
Phase 3: Auto-fixable Code Quality (~2 hours)¶
- Enable ESLint rules matching SonarCloud findings
- Run
eslint --fixacross the codebase - Review changes (especially
||→??conversions) - This should resolve ~350 minor issues
Phase 4: Manual Refactoring (ongoing)¶
- Reduce cognitive complexity in top offenders
- Extract nested ternaries to helper functions/variables
- Add
readonlyto constructor-injected NestJS fields - Move constant objects outside JSX render functions
- Replace array index keys with stable identifiers
Phase 5: Won't Fix Suppressions¶
- Add
// NOSONARto S7763 and S7772 issues (or suppress at quality profile level) - Review S1874 deprecated API usage and plan migration
Phase 6: Duplication Reduction (multi-sprint effort)¶
- D1–D5, D9: Extract shared infrastructure into
libs/service-common(observability, auth, config, event-log, notifications, audit, retry, webhook idempotency, versioning, tenancy) - D6: Move shared DTOs/events to
libs/domain-contracts(product mappings, print jobs, shipments) - D7–D8: Consolidate domain ownership (shipping-service owns shipments/SendCloud, print-service owns print jobs)
- Each phase = separate PR with full test coverage verification