@nifrajs/cli 3.5.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/dist/app-files.d.ts +11 -0
- package/dist/app-files.d.ts.map +1 -0
- package/dist/app-files.js +34 -0
- package/dist/app-files.js.map +1 -0
- package/dist/assure-hydration.d.ts +7 -1
- package/dist/assure-hydration.d.ts.map +1 -1
- package/dist/assure-hydration.js +128 -43
- package/dist/assure-hydration.js.map +1 -1
- package/dist/assure.d.ts.map +1 -1
- package/dist/assure.js +16 -2
- package/dist/assure.js.map +1 -1
- package/dist/capabilities-tool.d.ts +5 -1
- package/dist/capabilities-tool.d.ts.map +1 -1
- package/dist/capabilities-tool.js +32 -10
- package/dist/capabilities-tool.js.map +1 -1
- package/dist/cdn-check.d.ts +41 -0
- package/dist/cdn-check.d.ts.map +1 -0
- package/dist/cdn-check.js +207 -0
- package/dist/cdn-check.js.map +1 -0
- package/dist/check-diagnostics.d.ts +4 -0
- package/dist/check-diagnostics.d.ts.map +1 -1
- package/dist/check-diagnostics.js +13 -0
- package/dist/check-diagnostics.js.map +1 -1
- package/dist/check-scan.d.ts +49 -7
- package/dist/check-scan.d.ts.map +1 -1
- package/dist/check-scan.js +291 -62
- package/dist/check-scan.js.map +1 -1
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +99 -63
- package/dist/check.js.map +1 -1
- package/dist/cli.d.ts +42 -16
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +262 -82
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.d.ts +62 -5
- package/dist/command-catalog.d.ts.map +1 -1
- package/dist/command-catalog.js +275 -35
- package/dist/command-catalog.js.map +1 -1
- package/dist/config-target.d.ts +8 -0
- package/dist/config-target.d.ts.map +1 -0
- package/dist/config-target.js +45 -0
- package/dist/config-target.js.map +1 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +52 -13
- package/dist/contracts.js.map +1 -1
- package/dist/db-audit.d.ts +37 -0
- package/dist/db-audit.d.ts.map +1 -0
- package/dist/db-audit.js +99 -0
- package/dist/db-audit.js.map +1 -0
- package/dist/db-child.d.ts +64 -0
- package/dist/db-child.d.ts.map +1 -0
- package/dist/db-child.js +213 -0
- package/dist/db-child.js.map +1 -0
- package/dist/db-config.d.ts +60 -0
- package/dist/db-config.d.ts.map +1 -0
- package/dist/db-config.js +176 -0
- package/dist/db-config.js.map +1 -0
- package/dist/db-tool.d.ts +84 -0
- package/dist/db-tool.d.ts.map +1 -0
- package/dist/db-tool.js +537 -0
- package/dist/db-tool.js.map +1 -0
- package/dist/dev-bun-config.d.ts +20 -13
- package/dist/dev-bun-config.d.ts.map +1 -1
- package/dist/dev-bun-config.js +98 -41
- package/dist/dev-bun-config.js.map +1 -1
- package/dist/dev-feed-tool.d.ts +106 -0
- package/dist/dev-feed-tool.d.ts.map +1 -0
- package/dist/dev-feed-tool.js +713 -0
- package/dist/dev-feed-tool.js.map +1 -0
- package/dist/dev-server-client.d.ts +67 -0
- package/dist/dev-server-client.d.ts.map +1 -0
- package/dist/dev-server-client.js +195 -0
- package/dist/dev-server-client.js.map +1 -0
- package/dist/diff-tool.d.ts +3 -3
- package/dist/diff-tool.d.ts.map +1 -1
- package/dist/diff-tool.js +6 -5
- package/dist/diff-tool.js.map +1 -1
- package/dist/docs-search.d.ts.map +1 -1
- package/dist/docs-search.js +12 -4
- package/dist/docs-search.js.map +1 -1
- package/dist/doctor.d.ts +17 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +76 -15
- package/dist/doctor.js.map +1 -1
- package/dist/env-file.d.ts +15 -0
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +73 -1
- package/dist/env-file.js.map +1 -1
- package/dist/fix-recipes.d.ts.map +1 -1
- package/dist/fix-recipes.js +48 -22
- package/dist/fix-recipes.js.map +1 -1
- package/dist/frontend-guidance.js +5 -5
- package/dist/frontend-guidance.js.map +1 -1
- package/dist/i18n-check.d.ts +16 -0
- package/dist/i18n-check.d.ts.map +1 -0
- package/dist/i18n-check.js +154 -0
- package/dist/i18n-check.js.map +1 -0
- package/dist/init-agents.d.ts +49 -13
- package/dist/init-agents.d.ts.map +1 -1
- package/dist/init-agents.js +239 -34
- package/dist/init-agents.js.map +1 -1
- package/dist/internal/code-unit-order.d.ts +4 -0
- package/dist/internal/code-unit-order.d.ts.map +1 -0
- package/dist/internal/code-unit-order.js +6 -0
- package/dist/internal/code-unit-order.js.map +1 -0
- package/dist/internal/script-kind.d.ts +7 -0
- package/dist/internal/script-kind.d.ts.map +1 -0
- package/dist/internal/script-kind.js +12 -0
- package/dist/internal/script-kind.js.map +1 -0
- package/dist/internal/source-facts.d.ts +5 -0
- package/dist/internal/source-facts.d.ts.map +1 -1
- package/dist/internal/source-facts.js +61 -5
- package/dist/internal/source-facts.js.map +1 -1
- package/dist/internal/typescript-import.d.ts +49 -24
- package/dist/internal/typescript-import.d.ts.map +1 -1
- package/dist/internal/typescript-import.js +507 -67
- package/dist/internal/typescript-import.js.map +1 -1
- package/dist/introspect.d.ts +5 -1
- package/dist/introspect.d.ts.map +1 -1
- package/dist/introspect.js +92 -27
- package/dist/introspect.js.map +1 -1
- package/dist/learn.js +3 -3
- package/dist/learn.js.map +1 -1
- package/dist/levels-tool.d.ts +10 -0
- package/dist/levels-tool.d.ts.map +1 -1
- package/dist/levels-tool.js +33 -3
- package/dist/levels-tool.js.map +1 -1
- package/dist/load.d.ts +67 -17
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +78 -18
- package/dist/load.js.map +1 -1
- package/dist/manifest-tool.d.ts.map +1 -1
- package/dist/manifest-tool.js +3 -2
- package/dist/manifest-tool.js.map +1 -1
- package/dist/mcp-app-tool.d.ts.map +1 -1
- package/dist/mcp-app-tool.js +2 -0
- package/dist/mcp-app-tool.js.map +1 -1
- package/dist/mcp-context.js +1 -1
- package/dist/mcp-context.js.map +1 -1
- package/dist/mcp-delegate.d.ts +57 -0
- package/dist/mcp-delegate.d.ts.map +1 -0
- package/dist/mcp-delegate.js +130 -0
- package/dist/mcp-delegate.js.map +1 -0
- package/dist/mcp-docs-tools.d.ts.map +1 -1
- package/dist/mcp-docs-tools.js +23 -5
- package/dist/mcp-docs-tools.js.map +1 -1
- package/dist/mcp-exec.d.ts +2 -0
- package/dist/mcp-exec.d.ts.map +1 -1
- package/dist/mcp-exec.js +132 -108
- package/dist/mcp-exec.js.map +1 -1
- package/dist/mcp-http.d.ts.map +1 -1
- package/dist/mcp-http.js +17 -5
- package/dist/mcp-http.js.map +1 -1
- package/dist/mcp-io.d.ts +14 -0
- package/dist/mcp-io.d.ts.map +1 -1
- package/dist/mcp-io.js +45 -0
- package/dist/mcp-io.js.map +1 -1
- package/dist/mcp-isolate.d.ts +48 -0
- package/dist/mcp-isolate.d.ts.map +1 -0
- package/dist/mcp-isolate.js +222 -0
- package/dist/mcp-isolate.js.map +1 -0
- package/dist/mcp-project-child.d.ts +74 -0
- package/dist/mcp-project-child.d.ts.map +1 -0
- package/dist/mcp-project-child.js +130 -0
- package/dist/mcp-project-child.js.map +1 -0
- package/dist/mcp-render.js +2 -2
- package/dist/mcp-render.js.map +1 -1
- package/dist/mcp-root.d.ts +37 -9
- package/dist/mcp-root.d.ts.map +1 -1
- package/dist/mcp-root.js +110 -23
- package/dist/mcp-root.js.map +1 -1
- package/dist/mcp-run.d.ts +18 -1
- package/dist/mcp-run.d.ts.map +1 -1
- package/dist/mcp-run.js +82 -9
- package/dist/mcp-run.js.map +1 -1
- package/dist/mcp-ws.js +4 -1
- package/dist/mcp-ws.js.map +1 -1
- package/dist/mcp.d.ts +16 -3
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +81 -53
- package/dist/mcp.js.map +1 -1
- package/dist/migrate-layout.d.ts +44 -0
- package/dist/migrate-layout.d.ts.map +1 -0
- package/dist/migrate-layout.js +1155 -0
- package/dist/migrate-layout.js.map +1 -0
- package/dist/openapi-tool.d.ts +2 -2
- package/dist/openapi-tool.d.ts.map +1 -1
- package/dist/openapi-tool.js.map +1 -1
- package/dist/openapi-types.d.ts +2 -1
- package/dist/openapi-types.d.ts.map +1 -1
- package/dist/openapi-types.js +240 -12
- package/dist/openapi-types.js.map +1 -1
- package/dist/pipeline-report.d.ts +3 -2
- package/dist/pipeline-report.d.ts.map +1 -1
- package/dist/pipeline-report.js +19 -16
- package/dist/pipeline-report.js.map +1 -1
- package/dist/port.d.ts +5 -4
- package/dist/port.d.ts.map +1 -1
- package/dist/port.js +46 -29
- package/dist/port.js.map +1 -1
- package/dist/project-facts.d.ts +5 -0
- package/dist/project-facts.d.ts.map +1 -1
- package/dist/project-facts.js.map +1 -1
- package/dist/recipes/4.0.0.d.ts +9 -0
- package/dist/recipes/4.0.0.d.ts.map +1 -0
- package/dist/recipes/4.0.0.js +36 -0
- package/dist/recipes/4.0.0.js.map +1 -0
- package/dist/recipes/index.d.ts.map +1 -1
- package/dist/recipes/index.js +2 -0
- package/dist/recipes/index.js.map +1 -1
- package/dist/release-verification.d.ts +11 -1
- package/dist/release-verification.d.ts.map +1 -1
- package/dist/release-verification.js +47 -4
- package/dist/release-verification.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +12 -8
- package/dist/review.js.map +1 -1
- package/dist/route-types.d.ts +28 -0
- package/dist/route-types.d.ts.map +1 -0
- package/dist/route-types.js +106 -0
- package/dist/route-types.js.map +1 -0
- package/dist/rules/codes.d.ts +9 -0
- package/dist/rules/codes.d.ts.map +1 -1
- package/dist/rules/codes.js +9 -0
- package/dist/rules/codes.js.map +1 -1
- package/dist/rules/data-guard.d.ts +12 -0
- package/dist/rules/data-guard.d.ts.map +1 -0
- package/dist/rules/data-guard.js +213 -0
- package/dist/rules/data-guard.js.map +1 -0
- package/dist/rules/index.d.ts +3 -0
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +17 -5
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/islands.d.ts.map +1 -1
- package/dist/rules/islands.js +7 -4
- package/dist/rules/islands.js.map +1 -1
- package/dist/rules/legacy.d.ts.map +1 -1
- package/dist/rules/legacy.js +71 -38
- package/dist/rules/legacy.js.map +1 -1
- package/dist/rules/nano.d.ts.map +1 -1
- package/dist/rules/nano.js +15 -6
- package/dist/rules/nano.js.map +1 -1
- package/dist/rules/pages.d.ts +15 -0
- package/dist/rules/pages.d.ts.map +1 -0
- package/dist/rules/pages.js +88 -0
- package/dist/rules/pages.js.map +1 -0
- package/dist/rules/routes.d.ts +11 -0
- package/dist/rules/routes.d.ts.map +1 -1
- package/dist/rules/routes.js +180 -2
- package/dist/rules/routes.js.map +1 -1
- package/dist/rules/secrets.d.ts +10 -0
- package/dist/rules/secrets.d.ts.map +1 -0
- package/dist/rules/secrets.js +169 -0
- package/dist/rules/secrets.js.map +1 -0
- package/dist/rules/security.d.ts +7 -0
- package/dist/rules/security.d.ts.map +1 -1
- package/dist/rules/security.js +171 -65
- package/dist/rules/security.js.map +1 -1
- package/dist/rules/typescript.d.ts +16 -0
- package/dist/rules/typescript.d.ts.map +1 -0
- package/dist/rules/typescript.js +59 -0
- package/dist/rules/typescript.js.map +1 -0
- package/dist/rules/zones.d.ts +17 -0
- package/dist/rules/zones.d.ts.map +1 -0
- package/dist/rules/zones.js +174 -0
- package/dist/rules/zones.js.map +1 -0
- package/dist/scaffold.d.ts +25 -12
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +132 -87
- package/dist/scaffold.js.map +1 -1
- package/dist/sdk.d.ts +17 -3
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +941 -153
- package/dist/sdk.js.map +1 -1
- package/dist/search-terms.d.ts +2 -0
- package/dist/search-terms.d.ts.map +1 -1
- package/dist/search-terms.js +23 -8
- package/dist/search-terms.js.map +1 -1
- package/dist/smoke.d.ts +79 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +494 -0
- package/dist/smoke.js.map +1 -0
- package/dist/sync-manifest.d.ts +10 -2
- package/dist/sync-manifest.d.ts.map +1 -1
- package/dist/sync-manifest.js +44 -19
- package/dist/sync-manifest.js.map +1 -1
- package/dist/sync-routes.d.ts +4 -3
- package/dist/sync-routes.d.ts.map +1 -1
- package/dist/sync-routes.js +4 -3
- package/dist/sync-routes.js.map +1 -1
- package/dist/test-tool.d.ts.map +1 -1
- package/dist/test-tool.js +30 -11
- package/dist/test-tool.js.map +1 -1
- package/dist/types-search.d.ts.map +1 -1
- package/dist/types-search.js +11 -5
- package/dist/types-search.js.map +1 -1
- package/dist/upgrade.d.ts +36 -6
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +167 -29
- package/dist/upgrade.js.map +1 -1
- package/dist/verification-plan.d.ts.map +1 -1
- package/dist/verification-plan.js +4 -0
- package/dist/verification-plan.js.map +1 -1
- package/dist/verification.d.ts.map +1 -1
- package/dist/verification.js +45 -12
- package/dist/verification.js.map +1 -1
- package/dist/web-app-options.d.ts +19 -0
- package/dist/web-app-options.d.ts.map +1 -0
- package/dist/web-app-options.js +49 -0
- package/dist/web-app-options.js.map +1 -0
- package/dist/work-graph.d.ts.map +1 -1
- package/dist/work-graph.js +29 -13
- package/dist/work-graph.js.map +1 -1
- package/docs/examples.json +185 -29
- package/docs/llms-full.txt +3807 -580
- package/docs/types.json +14229 -10939
- package/package.json +19 -12
- package/src/app-files.ts +40 -0
- package/src/assure-hydration.ts +139 -46
- package/src/assure.ts +17 -2
- package/src/capabilities-tool.ts +35 -9
- package/src/cdn-check.ts +268 -0
- package/src/check-diagnostics.ts +17 -0
- package/src/check-scan.ts +358 -63
- package/src/check.ts +97 -62
- package/src/cli.ts +296 -87
- package/src/command-catalog.ts +389 -48
- package/src/config-target.ts +47 -0
- package/src/contracts.ts +54 -12
- package/src/db-audit.ts +128 -0
- package/src/db-child.ts +285 -0
- package/src/db-config.ts +224 -0
- package/src/db-tool.ts +690 -0
- package/src/dev-bun-config.ts +116 -40
- package/src/dev-feed-tool.ts +912 -0
- package/src/dev-server-client.ts +267 -0
- package/src/diff-tool.ts +6 -5
- package/src/docs-search.ts +10 -3
- package/src/doctor.ts +98 -15
- package/src/env-file.ts +78 -1
- package/src/fix-recipes.ts +53 -32
- package/src/frontend-guidance.ts +5 -5
- package/src/i18n-check.ts +179 -0
- package/src/init-agents.ts +301 -38
- package/src/internal/code-unit-order.ts +5 -0
- package/src/internal/script-kind.ts +11 -0
- package/src/internal/source-facts.ts +96 -22
- package/src/internal/typescript-import.ts +663 -68
- package/src/introspect.ts +90 -27
- package/src/learn.ts +3 -3
- package/src/levels-tool.ts +47 -2
- package/src/load.ts +140 -33
- package/src/manifest-tool.ts +3 -2
- package/src/mcp-app-tool.ts +2 -0
- package/src/mcp-context.ts +1 -1
- package/src/mcp-delegate.ts +162 -0
- package/src/mcp-docs-tools.ts +23 -5
- package/src/mcp-exec.ts +160 -132
- package/src/mcp-http.ts +19 -5
- package/src/mcp-io.ts +57 -0
- package/src/mcp-isolate.ts +287 -0
- package/src/mcp-project-child.ts +182 -0
- package/src/mcp-render.ts +2 -2
- package/src/mcp-root.ts +117 -22
- package/src/mcp-run.ts +104 -10
- package/src/mcp-ws.ts +4 -1
- package/src/mcp.ts +106 -64
- package/src/migrate-layout.ts +1338 -0
- package/src/openapi-tool.ts +7 -3
- package/src/openapi-types.ts +315 -13
- package/src/pipeline-report.ts +22 -18
- package/src/port.ts +53 -30
- package/src/project-facts.ts +5 -0
- package/src/recipes/4.0.0.ts +37 -0
- package/src/recipes/index.ts +2 -0
- package/src/release-verification.ts +74 -5
- package/src/review.ts +13 -8
- package/src/route-types.ts +133 -0
- package/src/rules/codes.ts +9 -0
- package/src/rules/data-guard.ts +230 -0
- package/src/rules/index.ts +18 -5
- package/src/rules/islands.ts +12 -4
- package/src/rules/legacy.ts +71 -39
- package/src/rules/nano.ts +18 -6
- package/src/rules/pages.ts +99 -0
- package/src/rules/routes.ts +181 -2
- package/src/rules/secrets.ts +174 -0
- package/src/rules/security.ts +198 -70
- package/src/rules/typescript.ts +77 -0
- package/src/rules/zones.ts +195 -0
- package/src/scaffold.ts +147 -95
- package/src/sdk.ts +1215 -158
- package/src/search-terms.ts +24 -7
- package/src/smoke.ts +607 -0
- package/src/sync-manifest.ts +50 -19
- package/src/sync-routes.ts +4 -3
- package/src/test-tool.ts +32 -11
- package/src/types-search.ts +11 -4
- package/src/upgrade.ts +205 -25
- package/src/verification-plan.ts +13 -0
- package/src/verification.ts +50 -12
- package/src/web-app-options.ts +57 -0
- package/src/work-graph.ts +27 -13
package/dist/rules/routes.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { RESERVED_KEY_READOUT, reservedKeyFor } from "@nifrajs/client";
|
|
2
|
+
import { RoutePatternOverlapLimitError, routePatternOverlap } from "@nifrajs/core";
|
|
3
|
+
import { expandOptionalParams, paramConstraint } from "@nifrajs/core/pattern";
|
|
2
4
|
import { diagnostic } from "../diagnostics.js";
|
|
3
5
|
import { commentBlockHasMarker } from "./comment-markers.js";
|
|
4
6
|
/**
|
|
@@ -20,6 +22,8 @@ import { commentBlockHasMarker } from "./comment-markers.js";
|
|
|
20
22
|
*/
|
|
21
23
|
/** Opt-out pragma for a route deliberately served only to NON-typed-client consumers. */
|
|
22
24
|
const RESERVED_SEGMENT_PRAGMA = "nifra-expect reserved-segment";
|
|
25
|
+
const ROUTE_OVERLAP_PRAGMA = "nifra-expect route-overlap";
|
|
26
|
+
const PARAM_MODIFIER_PRAGMA = "nifra-expect param-modifier";
|
|
23
27
|
/** Parse-don't-cast over the project fact: a malformed entry is dropped, never trusted. */
|
|
24
28
|
function routeFacts(ctx) {
|
|
25
29
|
const raw = ctx.project.routes;
|
|
@@ -37,6 +41,17 @@ function routeFacts(ctx) {
|
|
|
37
41
|
}
|
|
38
42
|
return out;
|
|
39
43
|
}
|
|
44
|
+
/** One fact per registration site, so `all()` or `method([...])` reports its path once, not per method. */
|
|
45
|
+
function pathSites(routes) {
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
return routes.filter((route) => {
|
|
48
|
+
const site = `${route.file}\n${route.line}\n${route.path}`;
|
|
49
|
+
if (seen.has(site))
|
|
50
|
+
return false;
|
|
51
|
+
seen.add(site);
|
|
52
|
+
return true;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
40
55
|
/**
|
|
41
56
|
* The typed escape spelling for a colliding segment, e.g. `/api/delete` + `delete` →
|
|
42
57
|
* `api("delete").post()` shown as the chain up to the collision. Best-effort readability: earlier
|
|
@@ -53,7 +68,7 @@ export const reservedSegmentRule = {
|
|
|
53
68
|
async scan(ctx) {
|
|
54
69
|
const findings = [];
|
|
55
70
|
const linesByFile = new Map();
|
|
56
|
-
for (const route of routeFacts(ctx)) {
|
|
71
|
+
for (const route of pathSites(routeFacts(ctx))) {
|
|
57
72
|
for (const segment of route.path.split("/")) {
|
|
58
73
|
if (segment === "")
|
|
59
74
|
continue;
|
|
@@ -98,6 +113,9 @@ export const duplicateRouteRule = {
|
|
|
98
113
|
async scan(ctx) {
|
|
99
114
|
const findings = [];
|
|
100
115
|
const byFile = new Map();
|
|
116
|
+
// Two calls that each register several methods collide once per shared method; that is one
|
|
117
|
+
// pair of lines to fix, so it is reported once.
|
|
118
|
+
const reported = new Set();
|
|
101
119
|
for (const route of routeFacts(ctx)) {
|
|
102
120
|
let seen = byFile.get(route.file);
|
|
103
121
|
if (seen === undefined) {
|
|
@@ -110,6 +128,10 @@ export const duplicateRouteRule = {
|
|
|
110
128
|
seen.set(key, route);
|
|
111
129
|
continue;
|
|
112
130
|
}
|
|
131
|
+
const pair = `${route.file}\n${route.line}\n${first.line}\n${route.path}`;
|
|
132
|
+
if (reported.has(pair))
|
|
133
|
+
continue;
|
|
134
|
+
reported.add(pair);
|
|
113
135
|
findings.push(diagnostic({
|
|
114
136
|
code: "NF-C019",
|
|
115
137
|
severity: "error",
|
|
@@ -123,5 +145,161 @@ export const duplicateRouteRule = {
|
|
|
123
145
|
return findings;
|
|
124
146
|
},
|
|
125
147
|
};
|
|
126
|
-
|
|
148
|
+
/**
|
|
149
|
+
* NF-C024: two different same-method route patterns in one source file accept at least one of the
|
|
150
|
+
* same paths. This is deliberately file-scoped, like NF-C019: the static scanner cannot prove
|
|
151
|
+
* which application instance owns routes collected from different files in a monorepo.
|
|
152
|
+
*/
|
|
153
|
+
export const overlappingRouteRule = {
|
|
154
|
+
code: "NF-C024",
|
|
155
|
+
title: "Overlapping route registration",
|
|
156
|
+
async scan(ctx) {
|
|
157
|
+
const findings = [];
|
|
158
|
+
const linesByFile = new Map();
|
|
159
|
+
const byFile = new Map();
|
|
160
|
+
for (const route of routeFacts(ctx)) {
|
|
161
|
+
const routes = byFile.get(route.file);
|
|
162
|
+
if (routes === undefined)
|
|
163
|
+
byFile.set(route.file, [route]);
|
|
164
|
+
else
|
|
165
|
+
routes.push(route);
|
|
166
|
+
}
|
|
167
|
+
// As in NF-C019: two multi-method calls overlap once per shared method, reported once.
|
|
168
|
+
const reported = new Set();
|
|
169
|
+
for (const [file, routes] of byFile) {
|
|
170
|
+
const lines = () => {
|
|
171
|
+
let value = linesByFile.get(file);
|
|
172
|
+
if (value === undefined) {
|
|
173
|
+
value = (ctx.project.source.read(file) ?? "").split("\n");
|
|
174
|
+
linesByFile.set(file, value);
|
|
175
|
+
}
|
|
176
|
+
return value;
|
|
177
|
+
};
|
|
178
|
+
for (let laterIndex = 0; laterIndex < routes.length; laterIndex += 1) {
|
|
179
|
+
const later = routes[laterIndex];
|
|
180
|
+
const laterKey = `${later.method} ${later.path}`;
|
|
181
|
+
for (let earlierIndex = 0; earlierIndex < laterIndex; earlierIndex += 1) {
|
|
182
|
+
const earlier = routes[earlierIndex];
|
|
183
|
+
if (earlier.method !== later.method || `${earlier.method} ${earlier.path}` === laterKey)
|
|
184
|
+
continue;
|
|
185
|
+
if (commentBlockHasMarker(lines(), later.line, ROUTE_OVERLAP_PRAGMA))
|
|
186
|
+
continue;
|
|
187
|
+
if (commentBlockHasMarker(lines(), earlier.line, ROUTE_OVERLAP_PRAGMA))
|
|
188
|
+
continue;
|
|
189
|
+
let witness;
|
|
190
|
+
try {
|
|
191
|
+
witness = routePatternOverlap(earlier.path, later.path);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (error instanceof RoutePatternOverlapLimitError) {
|
|
195
|
+
findings.push(diagnostic({
|
|
196
|
+
code: "NF-C025",
|
|
197
|
+
severity: "error",
|
|
198
|
+
file: later.file,
|
|
199
|
+
line: later.line,
|
|
200
|
+
message: `${laterKey} could not be proven disjoint from ${earlier.method} ${earlier.path}; bounded route-overlap analysis exceeded its safety budget - simplify the patterns or add the ${ROUTE_OVERLAP_PRAGMA} comment only after manual review`,
|
|
201
|
+
evidence: [
|
|
202
|
+
`${earlier.method} ${earlier.path}`,
|
|
203
|
+
`${later.method} ${later.path}`,
|
|
204
|
+
`first registration: line ${earlier.line}`,
|
|
205
|
+
],
|
|
206
|
+
verify: "nifra check --lints-only",
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
// The primary route parser diagnostic owns malformed route patterns. A secondary lint
|
|
210
|
+
// must remain total and never turn an invalid route into a checker crash.
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (witness === undefined)
|
|
214
|
+
continue;
|
|
215
|
+
const pair = `${file}\n${later.line}\n${later.path}\n${earlier.line}\n${earlier.path}`;
|
|
216
|
+
if (reported.has(pair))
|
|
217
|
+
break;
|
|
218
|
+
reported.add(pair);
|
|
219
|
+
findings.push(diagnostic({
|
|
220
|
+
code: "NF-C024",
|
|
221
|
+
severity: "error",
|
|
222
|
+
file: later.file,
|
|
223
|
+
line: later.line,
|
|
224
|
+
message: `${laterKey} overlaps ${earlier.method} ${earlier.path} (first at line ${earlier.line}); witness path: ${witness} - make the patterns disjoint, order them intentionally with a pragma, or add the ${ROUTE_OVERLAP_PRAGMA} comment above either registration`,
|
|
225
|
+
evidence: [
|
|
226
|
+
`${earlier.method} ${earlier.path}`,
|
|
227
|
+
`${later.method} ${later.path}`,
|
|
228
|
+
`witness: ${witness}`,
|
|
229
|
+
`first registration: line ${earlier.line}`,
|
|
230
|
+
],
|
|
231
|
+
verify: "nifra check --lints-only",
|
|
232
|
+
}));
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return findings;
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
/** A param name directly followed by a character other routers read as a modifier. */
|
|
241
|
+
const PARAM_MODIFIER = /:[A-Za-z_][A-Za-z0-9_]*[?*+{(<]/g;
|
|
242
|
+
/**
|
|
243
|
+
* The first param in `path` whose modifier the router reads as literal text. A `{...}` group the
|
|
244
|
+
* router reads as a constraint (`:id{[0-9]+}`, `:ext{png|jpg}`) is syntax, so it is passed over.
|
|
245
|
+
*/
|
|
246
|
+
function literalModifier(path) {
|
|
247
|
+
PARAM_MODIFIER.lastIndex = 0;
|
|
248
|
+
for (let found = PARAM_MODIFIER.exec(path); found !== null; found = PARAM_MODIFIER.exec(path)) {
|
|
249
|
+
const text = found[0];
|
|
250
|
+
if (!text.endsWith("{"))
|
|
251
|
+
return text;
|
|
252
|
+
const constraint = paramConstraint(path.slice(PARAM_MODIFIER.lastIndex - 1));
|
|
253
|
+
if (constraint === undefined)
|
|
254
|
+
return text;
|
|
255
|
+
const end = PARAM_MODIFIER.lastIndex + constraint.source.length + 1;
|
|
256
|
+
// A modifier after the constraint is literal text like any other (`:id{[0-9]+}?/posts`).
|
|
257
|
+
if ("?*+{(<".includes(path[end] ?? "/"))
|
|
258
|
+
return path.slice(found.index, end + 1);
|
|
259
|
+
PARAM_MODIFIER.lastIndex = end;
|
|
260
|
+
}
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* NF-C026: a param followed by `?`, `*`, `+`, `{`, `(` or `<` that the router reads as literal text
|
|
265
|
+
* (`/users/:id?/posts`, `/users/:id{int}`). Only a trailing `?` run and a valid `{...}` are syntax.
|
|
266
|
+
*/
|
|
267
|
+
export const paramModifierRule = {
|
|
268
|
+
code: "NF-C026",
|
|
269
|
+
title: "Route param followed by an unsupported modifier",
|
|
270
|
+
async scan(ctx) {
|
|
271
|
+
const findings = [];
|
|
272
|
+
const linesByFile = new Map();
|
|
273
|
+
for (const route of pathSites(routeFacts(ctx))) {
|
|
274
|
+
const forms = expandOptionalParams(route.path);
|
|
275
|
+
const text = literalModifier(forms[forms.length - 1] ?? route.path);
|
|
276
|
+
if (text === undefined)
|
|
277
|
+
continue;
|
|
278
|
+
let lines = linesByFile.get(route.file);
|
|
279
|
+
if (lines === undefined) {
|
|
280
|
+
lines = (ctx.project.source.read(route.file) ?? "").split("\n");
|
|
281
|
+
linesByFile.set(route.file, lines);
|
|
282
|
+
}
|
|
283
|
+
if (commentBlockHasMarker(lines, route.line, PARAM_MODIFIER_PRAGMA))
|
|
284
|
+
continue;
|
|
285
|
+
const character = text[text.length - 1];
|
|
286
|
+
findings.push(diagnostic({
|
|
287
|
+
code: "NF-C026",
|
|
288
|
+
severity: "warn",
|
|
289
|
+
file: route.file,
|
|
290
|
+
line: route.line,
|
|
291
|
+
message: `${route.method} ${route.path} - '${character}' after '${text.slice(0, -1)}' is matched as literal text, not as a param modifier${character === "?" ? "; a request path never contains '?', so this route cannot be reached" : ""}. Optional params are supported as the trailing whole segments of a path (\`/users/:id?\`, \`/d/:year?/:month?\`), and a constraint as one character class with an optional count (\`:id{[0-9]+}\`, \`:code{[A-Z]{2}}\`) or a list of two or more values (\`:ext{png|jpg}\`); for anything else register each path, or mark a deliberate literal with \`// ${PARAM_MODIFIER_PRAGMA}\` above the registration`,
|
|
292
|
+
evidence: [`${route.method} ${route.path}`, `literal: ${text}`],
|
|
293
|
+
verify: "nifra check --lints-only",
|
|
294
|
+
}));
|
|
295
|
+
}
|
|
296
|
+
return findings;
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
export const routeRules = Object.freeze([
|
|
300
|
+
reservedSegmentRule,
|
|
301
|
+
duplicateRouteRule,
|
|
302
|
+
overlappingRouteRule,
|
|
303
|
+
paramModifierRule,
|
|
304
|
+
]);
|
|
127
305
|
//# sourceMappingURL=routes.js.map
|
package/dist/rules/routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/rules/routes.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/rules/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,6BAA6B,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,EAAmB,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAG5D;;;;;;;;;;;;;;;;GAgBG;AAEH,yFAAyF;AACzF,MAAM,uBAAuB,GAAG,+BAA+B,CAAA;AAC/D,MAAM,oBAAoB,GAAG,4BAA4B,CAAA;AACzD,MAAM,qBAAqB,GAAG,6BAA6B,CAAA;AAS3D,2FAA2F;AAC3F,SAAS,UAAU,CAAC,GAAgB;IAClC,MAAM,GAAG,GAAuB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAA;IAClD,MAAM,GAAG,GAAsB,EAAE,CAAA;IACjC,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC9E,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QACvD,IACE,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAC/B,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAC/B,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;YACjC,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAE/B,SAAQ;QACV,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAC9F,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,2GAA2G;AAC3G,SAAS,SAAS,CAAC,MAAkC;IACnD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAA;QAC1D,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAChC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACd,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,SAAiB;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC,CAAA;IACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAC1E,OAAO,GAAG,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,CAAA;AAC1D,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAc;IAC5C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,yDAAyD;IAChE,KAAK,CAAC,IAAI,CAAC,GAAG;QACZ,MAAM,QAAQ,GAAiB,EAAE,CAAA;QACjC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6B,CAAA;QACxD,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC/C,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5C,IAAI,OAAO,KAAK,EAAE;oBAAE,SAAQ;gBAC5B,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;gBACzC,IAAI,SAAS,KAAK,SAAS;oBAAE,SAAQ;gBACrC,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBAC/D,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gBACpC,CAAC;gBACD,IAAI,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,uBAAuB,CAAC;oBAAE,SAAQ;gBAC/E,QAAQ,CAAC,IAAI,CACX,UAAU,CAAC;oBACT,IAAI,EAAE,SAAS;oBACf,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,eAAe,OAAO,kDAAkD,SAAS,gBAAgB,oBAAoB,oFAAoF,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,sFAAsF,uBAAuB,gGAAgG;oBAChe,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,YAAY,OAAO,EAAE,CAAC;oBAClE,sFAAsF;oBACtF,qFAAqF;oBACrF,GAAG,EAAE,EAAE,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,0BAA0B,EAAE;oBAC/E,MAAM,EAAE,0BAA0B;iBACnC,CAAC,CACH,CAAA;gBACD,MAAK,CAAC,0DAA0D;YAClE,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAc;IAC3C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,8BAA8B;IACrC,KAAK,CAAC,IAAI,CAAC,GAAG;QACZ,MAAM,QAAQ,GAAiB,EAAE,CAAA;QACjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwC,CAAA;QAC9D,2FAA2F;QAC3F,gDAAgD;QAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;QAClC,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpC,IAAI,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACjC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAA;gBAChB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAC9B,CAAC;YACD,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAA;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC3B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBACpB,SAAQ;YACV,CAAC;YACD,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAA;YACzE,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAQ;YAChC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAClB,QAAQ,CAAC,IAAI,CACX,UAAU,CAAC;gBACT,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EAAE,GAAG,GAAG,oDAAoD,KAAK,CAAC,IAAI,kEAAkE;gBAC/I,QAAQ,EAAE,CAAC,GAAG,EAAE,4BAA4B,KAAK,CAAC,IAAI,EAAE,CAAC;gBACzD,MAAM,EAAE,0BAA0B;aACnC,CAAC,CACH,CAAA;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAc;IAC7C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,gCAAgC;IACvC,KAAK,CAAC,IAAI,CAAC,GAAG;QACZ,MAAM,QAAQ,GAAiB,EAAE,CAAA;QACjC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6B,CAAA;QACxD,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAA;QACnD,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACrC,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;;gBACpD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;QAED,uFAAuF;QACvF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;QAClC,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,GAAsB,EAAE;gBACpC,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;gBACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBACzD,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gBAC9B,CAAC;gBACD,OAAO,KAAK,CAAA;YACd,CAAC,CAAA;YAED,KAAK,IAAI,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,IAAI,CAAC,EAAE,CAAC;gBACrE,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAE,CAAA;gBACjC,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAA;gBAChD,KAAK,IAAI,YAAY,GAAG,CAAC,EAAE,YAAY,GAAG,UAAU,EAAE,YAAY,IAAI,CAAC,EAAE,CAAC;oBACxE,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAE,CAAA;oBACrC,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,QAAQ;wBACrF,SAAQ;oBACV,IAAI,qBAAqB,CAAC,KAAK,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,oBAAoB,CAAC;wBAAE,SAAQ;oBAC9E,IAAI,qBAAqB,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,IAAI,EAAE,oBAAoB,CAAC;wBAAE,SAAQ;oBAEhF,IAAI,OAA2B,CAAA;oBAC/B,IAAI,CAAC;wBACH,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;oBACzD,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,IAAI,KAAK,YAAY,6BAA6B,EAAE,CAAC;4BACnD,QAAQ,CAAC,IAAI,CACX,UAAU,CAAC;gCACT,IAAI,EAAE,SAAS;gCACf,QAAQ,EAAE,OAAO;gCACjB,IAAI,EAAE,KAAK,CAAC,IAAI;gCAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gCAChB,OAAO,EAAE,GAAG,QAAQ,sCAAsC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,kGAAkG,oBAAoB,mCAAmC;gCACjP,QAAQ,EAAE;oCACR,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,EAAE;oCACnC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE;oCAC/B,4BAA4B,OAAO,CAAC,IAAI,EAAE;iCAC3C;gCACD,MAAM,EAAE,0BAA0B;6BACnC,CAAC,CACH,CAAA;wBACH,CAAC;wBACD,sFAAsF;wBACtF,0EAA0E;wBAC1E,SAAQ;oBACV,CAAC;oBACD,IAAI,OAAO,KAAK,SAAS;wBAAE,SAAQ;oBACnC,MAAM,IAAI,GAAG,GAAG,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,EAAE,CAAA;oBACtF,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;wBAAE,MAAK;oBAC7B,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;oBAElB,QAAQ,CAAC,IAAI,CACX,UAAU,CAAC;wBACT,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,OAAO,EAAE,GAAG,QAAQ,aAAa,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,mBAAmB,OAAO,CAAC,IAAI,oBAAoB,OAAO,qFAAqF,oBAAoB,oCAAoC;wBACtQ,QAAQ,EAAE;4BACR,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,EAAE;4BACnC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE;4BAC/B,YAAY,OAAO,EAAE;4BACrB,4BAA4B,OAAO,CAAC,IAAI,EAAE;yBAC3C;wBACD,MAAM,EAAE,0BAA0B;qBACnC,CAAC,CACH,CAAA;oBACD,MAAK;gBACP,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED,sFAAsF;AACtF,MAAM,cAAc,GAAG,kCAAkC,CAAA;AAEzD;;;GAGG;AACH,SAAS,eAAe,CAAC,IAAY;IACnC,cAAc,CAAC,SAAS,GAAG,CAAC,CAAA;IAC5B,KAAK,IAAI,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9F,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QACpC,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAA;QAC5E,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,IAAI,CAAA;QACzC,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;QACnE,yFAAyF;QACzF,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;QAChF,cAAc,CAAC,SAAS,GAAG,GAAG,CAAA;IAChC,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAc;IAC1C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,iDAAiD;IACxD,KAAK,CAAC,IAAI,CAAC,GAAG;QACZ,MAAM,QAAQ,GAAiB,EAAE,CAAA;QACjC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6B,CAAA;QACxD,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC/C,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAC9C,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAA;YACnE,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAQ;YAChC,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAC/D,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACpC,CAAC;YACD,IAAI,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,qBAAqB,CAAC;gBAAE,SAAQ;YAC7E,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAA;YACxC,QAAQ,CAAC,IAAI,CACX,UAAU,CAAC;gBACT,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,OAAO,SAAS,YAAY,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,wDAAwD,SAAS,KAAK,GAAG,CAAC,CAAC,CAAC,sEAAsE,CAAC,CAAC,CAAC,EAAE,8VAA8V,qBAAqB,2BAA2B;gBACxnB,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,YAAY,IAAI,EAAE,CAAC;gBAC/D,MAAM,EAAE,0BAA0B;aACnC,CAAC,CACH,CAAA;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACtC,mBAAmB;IACnB,kBAAkB;IAClB,oBAAoB;IACpB,iBAAiB;CAClB,CAAC,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type SecretExemption } from "@nifrajs/web/zones";
|
|
2
|
+
import type { CheckRule } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The app's `secretExemptions`, read statically from `nifra.config.ts` (check never imports app code):
|
|
5
|
+
* an array literal of object literals with string fields, the form the build error suggests.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readSecretExemptions(appRoot: string): SecretExemption[];
|
|
8
|
+
export declare const secretSourceRule: CheckRule;
|
|
9
|
+
export declare const secretRules: readonly CheckRule[];
|
|
10
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/rules/secrets.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,eAAe,EAGrB,MAAM,oBAAoB,CAAA;AAK3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAkD3C;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CA0CvE;AAED,eAAO,MAAM,gBAAgB,EAAE,SAuD9B,CAAA;AAED,eAAO,MAAM,WAAW,sBAAoC,CAAA"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NF-C032: what looks like a credential in browser code or in `public/`, checked from source with the
|
|
3
|
+
* scanner every client build runs (`@nifrajs/web/zones`). The build also matches the values of the
|
|
4
|
+
* build environment's private variables in its output; that needs the build's environment, so it
|
|
5
|
+
* stays the build's.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { publicScanFiles, scanForSecrets, } from "@nifrajs/web/zones";
|
|
10
|
+
import { CONFIG_FILE } from "../app-files.js";
|
|
11
|
+
import { stripComments } from "../check-scan.js";
|
|
12
|
+
import { diagnostic } from "../diagnostics.js";
|
|
13
|
+
import { codeUnitOrder } from "../internal/code-unit-order.js";
|
|
14
|
+
import { zonedFiles } from "./zones.js";
|
|
15
|
+
const BROWSER_CODE = new Set(["route-frontend", "frontend", "shared"]);
|
|
16
|
+
const EXEMPTIONS_START = /\bexport\s+const\s+secretExemptions\b[^=]*=\s*\[/;
|
|
17
|
+
/**
|
|
18
|
+
* The tokens of an array literal, from just after its `[` to the `]` that closes it. A string is one
|
|
19
|
+
* token, so a bracket or a colon inside one (`"routes/[lang]/index.tsx"`) is text, never syntax.
|
|
20
|
+
*/
|
|
21
|
+
function arrayTokens(src, from) {
|
|
22
|
+
const tokens = [];
|
|
23
|
+
let depth = 0;
|
|
24
|
+
let i = from;
|
|
25
|
+
while (i < src.length) {
|
|
26
|
+
const c = src[i];
|
|
27
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
28
|
+
let value = "";
|
|
29
|
+
let j = i + 1;
|
|
30
|
+
while (j < src.length && src[j] !== c) {
|
|
31
|
+
if (src[j] === "\\") {
|
|
32
|
+
value += src[j + 1] ?? "";
|
|
33
|
+
j += 2;
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
value += src[j++];
|
|
37
|
+
}
|
|
38
|
+
tokens.push({ kind: "string", value });
|
|
39
|
+
i = j + 1;
|
|
40
|
+
}
|
|
41
|
+
else if (/[\w$]/.test(c)) {
|
|
42
|
+
let j = i;
|
|
43
|
+
while (j < src.length && /[\w$]/.test(src[j]))
|
|
44
|
+
j++;
|
|
45
|
+
tokens.push({ kind: "word", value: src.slice(i, j) });
|
|
46
|
+
i = j;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
if (c === "]" || c === "}" || c === ")") {
|
|
50
|
+
if (depth === 0)
|
|
51
|
+
break;
|
|
52
|
+
depth--;
|
|
53
|
+
}
|
|
54
|
+
else if (c === "[" || c === "{" || c === "(")
|
|
55
|
+
depth++;
|
|
56
|
+
if (!/\s/.test(c))
|
|
57
|
+
tokens.push({ kind: "punct", value: c });
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return tokens;
|
|
62
|
+
}
|
|
63
|
+
const isPunct = (token, value) => token?.kind === "punct" && token.value === value;
|
|
64
|
+
/**
|
|
65
|
+
* The app's `secretExemptions`, read statically from `nifra.config.ts` (check never imports app code):
|
|
66
|
+
* an array literal of object literals with string fields, the form the build error suggests.
|
|
67
|
+
*/
|
|
68
|
+
export function readSecretExemptions(appRoot) {
|
|
69
|
+
const path = join(appRoot, CONFIG_FILE);
|
|
70
|
+
if (!existsSync(path))
|
|
71
|
+
return [];
|
|
72
|
+
const text = stripComments(readFileSync(path, "utf8"));
|
|
73
|
+
const opened = EXEMPTIONS_START.exec(text);
|
|
74
|
+
if (opened === null)
|
|
75
|
+
return [];
|
|
76
|
+
const tokens = arrayTokens(text, opened.index + opened[0].length);
|
|
77
|
+
const exemptions = [];
|
|
78
|
+
let depth = 0;
|
|
79
|
+
let fields;
|
|
80
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
81
|
+
const token = tokens[i];
|
|
82
|
+
if (token.kind === "punct" && "[{(".includes(token.value)) {
|
|
83
|
+
depth++;
|
|
84
|
+
if (depth === 1 && token.value === "{")
|
|
85
|
+
fields = {};
|
|
86
|
+
}
|
|
87
|
+
else if (token.kind === "punct" && "]})".includes(token.value)) {
|
|
88
|
+
if (depth === 1 && fields !== undefined) {
|
|
89
|
+
const { rule, reason, file, env } = fields;
|
|
90
|
+
if (rule !== undefined && reason !== undefined) {
|
|
91
|
+
exemptions.push({
|
|
92
|
+
rule: rule,
|
|
93
|
+
reason,
|
|
94
|
+
...(file === undefined ? {} : { file }),
|
|
95
|
+
...(env === undefined ? {} : { env }),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
fields = undefined;
|
|
99
|
+
}
|
|
100
|
+
depth--;
|
|
101
|
+
}
|
|
102
|
+
else if (depth === 1 &&
|
|
103
|
+
fields !== undefined &&
|
|
104
|
+
token.kind !== "punct" &&
|
|
105
|
+
isPunct(tokens[i + 1], ":") &&
|
|
106
|
+
tokens[i + 2]?.kind === "string" &&
|
|
107
|
+
(isPunct(tokens[i + 3], ",") || isPunct(tokens[i + 3], "}"))) {
|
|
108
|
+
fields[token.value] ??= tokens[i + 2].value;
|
|
109
|
+
i += 2;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return exemptions;
|
|
113
|
+
}
|
|
114
|
+
export const secretSourceRule = {
|
|
115
|
+
code: "NF-C032",
|
|
116
|
+
title: "Credential in browser code",
|
|
117
|
+
async scan(ctx) {
|
|
118
|
+
const { files } = zonedFiles(ctx);
|
|
119
|
+
const apps = new Map();
|
|
120
|
+
for (const { file, app, classification } of files) {
|
|
121
|
+
if (!apps.has(app))
|
|
122
|
+
apps.set(app, []);
|
|
123
|
+
if (!BROWSER_CODE.has(classification.zone))
|
|
124
|
+
continue;
|
|
125
|
+
const text = ctx.sources.read(file);
|
|
126
|
+
if (text === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
const name = app === "" ? file : file.slice(app.length + 1);
|
|
129
|
+
apps.get(app)?.push({ name, text, firstParty: true });
|
|
130
|
+
}
|
|
131
|
+
const findings = [];
|
|
132
|
+
for (const [app, sources] of apps) {
|
|
133
|
+
const appRoot = join(ctx.root, app);
|
|
134
|
+
const publicDir = join(appRoot, "public");
|
|
135
|
+
try {
|
|
136
|
+
const found = scanForSecrets({
|
|
137
|
+
sources,
|
|
138
|
+
artifacts: existsSync(publicDir) ? publicScanFiles(publicDir, "public") : [],
|
|
139
|
+
exemptions: readSecretExemptions(appRoot),
|
|
140
|
+
});
|
|
141
|
+
for (const finding of found) {
|
|
142
|
+
const file = app === "" ? finding.file : `${app}/${finding.file}`;
|
|
143
|
+
findings.push(diagnostic({
|
|
144
|
+
code: "NF-C032",
|
|
145
|
+
severity: "error",
|
|
146
|
+
file,
|
|
147
|
+
line: finding.line,
|
|
148
|
+
message: `${file} carries what looks like a credential (${finding.what}), and it reaches the browser. Read it from the environment in a route's backend half or under backend/, and rotate it if it was ever committed`,
|
|
149
|
+
evidence: [`rule: ${finding.rule}`, `match: ${finding.preview}`],
|
|
150
|
+
verify: "nifra check",
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
// A malformed exemption: the build refuses it with the same message.
|
|
156
|
+
findings.push(diagnostic({
|
|
157
|
+
code: "NF-C032",
|
|
158
|
+
severity: "error",
|
|
159
|
+
file: join(app, CONFIG_FILE),
|
|
160
|
+
message: error instanceof Error ? error.message : String(error),
|
|
161
|
+
verify: "nifra check",
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return findings.sort((a, b) => codeUnitOrder(a.file ?? "", b.file ?? "") || (a.line ?? 0) - (b.line ?? 0));
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
export const secretRules = Object.freeze([secretSourceRule]);
|
|
169
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/rules/secrets.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/security.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import type * as TSApi from "typescript";
|
|
1
2
|
import type { CheckRule } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Rewrite every unreviewed NF-S002 comparison in one file to `nifraTimingSafeEqual(left, right)` and
|
|
5
|
+
* add the helper once. Sites come from the syntax tree, not a reported line: the helper lands above
|
|
6
|
+
* them, so a line number a check run reported no longer names the same statement after the first fix.
|
|
7
|
+
*/
|
|
8
|
+
export declare function rewriteSecretComparisons(ts: typeof TSApi, file: string, source: string): string;
|
|
2
9
|
export declare const secretComparisonRule: CheckRule;
|
|
3
10
|
export declare const piiLogRule: CheckRule;
|
|
4
11
|
export declare const failOpenGateRule: CheckRule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/rules/security.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/rules/security.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAKxC,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAA;AAoMxD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA+B/F;AAED,eAAO,MAAM,oBAAoB,EAAE,SAwClC,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,SAgDxB,CAAA;AA0BD,eAAO,MAAM,gBAAgB,EAAE,SA+D9B,CAAA;AAiBD,eAAO,MAAM,uBAAuB,EAAE,SA6DrC,CAAA;AAED,eAAO,MAAM,oBAAoB,EAAE,SAgDlC,CAAA;AAWD,eAAO,MAAM,wBAAwB,EAAE,SAyCtC,CAAA;AAID,eAAO,MAAM,0BAA0B,EAAE,SAwDxC,CAAA;AAED,eAAO,MAAM,aAAa,sBAQxB,CAAA"}
|