@kernlang/review 3.3.9 → 3.4.0

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.
Files changed (97) hide show
  1. package/dist/call-graph.d.ts +10 -0
  2. package/dist/call-graph.js +138 -9
  3. package/dist/call-graph.js.map +1 -1
  4. package/dist/concept-rules/auth-drift.js +2 -0
  5. package/dist/concept-rules/auth-drift.js.map +1 -1
  6. package/dist/concept-rules/auth-propagation-drift.js +2 -0
  7. package/dist/concept-rules/auth-propagation-drift.js.map +1 -1
  8. package/dist/concept-rules/body-shape-drift.d.ts +1 -1
  9. package/dist/concept-rules/body-shape-drift.js +5 -3
  10. package/dist/concept-rules/body-shape-drift.js.map +1 -1
  11. package/dist/concept-rules/contract-drift.js +3 -1
  12. package/dist/concept-rules/contract-drift.js.map +1 -1
  13. package/dist/concept-rules/contract-method-drift.js +2 -0
  14. package/dist/concept-rules/contract-method-drift.js.map +1 -1
  15. package/dist/concept-rules/index.js +2 -33
  16. package/dist/concept-rules/index.js.map +1 -1
  17. package/dist/concept-rules/request-validation-drift.js +3 -0
  18. package/dist/concept-rules/request-validation-drift.js.map +1 -1
  19. package/dist/concept-rules/root-cause.d.ts +4 -0
  20. package/dist/concept-rules/root-cause.js +31 -0
  21. package/dist/concept-rules/root-cause.js.map +1 -0
  22. package/dist/concept-rules/unbounded-collection-query.js +2 -0
  23. package/dist/concept-rules/unbounded-collection-query.js.map +1 -1
  24. package/dist/concept-rules/unhandled-api-error-shape.js +2 -0
  25. package/dist/concept-rules/unhandled-api-error-shape.js.map +1 -1
  26. package/dist/default-export.d.ts +41 -0
  27. package/dist/default-export.js +76 -0
  28. package/dist/default-export.js.map +1 -0
  29. package/dist/eval.d.ts +67 -0
  30. package/dist/eval.js +177 -0
  31. package/dist/eval.js.map +1 -0
  32. package/dist/file-context.js +32 -13
  33. package/dist/file-context.js.map +1 -1
  34. package/dist/file-role.d.ts +6 -0
  35. package/dist/file-role.js +27 -0
  36. package/dist/file-role.js.map +1 -1
  37. package/dist/framework-seeds.d.ts +46 -0
  38. package/dist/framework-seeds.js +245 -0
  39. package/dist/framework-seeds.js.map +1 -0
  40. package/dist/git-env.d.ts +1 -0
  41. package/dist/git-env.js +25 -0
  42. package/dist/git-env.js.map +1 -0
  43. package/dist/graph.js +246 -21
  44. package/dist/graph.js.map +1 -1
  45. package/dist/index.d.ts +10 -2
  46. package/dist/index.js +200 -56
  47. package/dist/index.js.map +1 -1
  48. package/dist/mappers/ts-concepts.js +87 -20
  49. package/dist/mappers/ts-concepts.js.map +1 -1
  50. package/dist/path-canonical.d.ts +34 -0
  51. package/dist/path-canonical.js +85 -0
  52. package/dist/path-canonical.js.map +1 -0
  53. package/dist/policy.d.ts +22 -0
  54. package/dist/policy.js +47 -0
  55. package/dist/policy.js.map +1 -0
  56. package/dist/project-context.d.ts +135 -0
  57. package/dist/project-context.js +563 -0
  58. package/dist/project-context.js.map +1 -0
  59. package/dist/public-api.d.ts +21 -0
  60. package/dist/public-api.js +17 -2
  61. package/dist/public-api.js.map +1 -1
  62. package/dist/reporter.js +22 -0
  63. package/dist/reporter.js.map +1 -1
  64. package/dist/rule-quality.d.ts +58 -0
  65. package/dist/rule-quality.js +357 -0
  66. package/dist/rule-quality.js.map +1 -0
  67. package/dist/rules/dead-code.d.ts +2 -2
  68. package/dist/rules/dead-code.js +88 -4
  69. package/dist/rules/dead-code.js.map +1 -1
  70. package/dist/rules/index.d.ts +22 -0
  71. package/dist/rules/index.js +32 -0
  72. package/dist/rules/index.js.map +1 -1
  73. package/dist/rules/kern-source.d.ts +4 -0
  74. package/dist/rules/kern-source.js +183 -0
  75. package/dist/rules/kern-source.js.map +1 -1
  76. package/dist/rules/react.js +52 -3
  77. package/dist/rules/react.js.map +1 -1
  78. package/dist/rules/suggest-kern-primitive.js +0 -1
  79. package/dist/rules/suggest-kern-primitive.js.map +1 -1
  80. package/dist/semantic-diff.js +2 -0
  81. package/dist/semantic-diff.js.map +1 -1
  82. package/dist/suppression/apply-suppression.js +2 -0
  83. package/dist/suppression/apply-suppression.js.map +1 -1
  84. package/dist/suppression/parse-directives.d.ts +13 -5
  85. package/dist/suppression/parse-directives.js +62 -8
  86. package/dist/suppression/parse-directives.js.map +1 -1
  87. package/dist/suppression/types.d.ts +9 -0
  88. package/dist/suppression/types.js +6 -1
  89. package/dist/suppression/types.js.map +1 -1
  90. package/dist/taint-crossfile.js +15 -8
  91. package/dist/taint-crossfile.js.map +1 -1
  92. package/dist/telemetry.d.ts +126 -0
  93. package/dist/telemetry.js +303 -0
  94. package/dist/telemetry.js.map +1 -0
  95. package/dist/types.d.ts +165 -1
  96. package/dist/types.js.map +1 -1
  97. package/package.json +4 -3
@@ -0,0 +1,22 @@
1
+ import type { ReviewConfig, ReviewPolicy } from './types.js';
2
+ export interface ReviewPolicyProfile {
3
+ policy: ReviewPolicy;
4
+ crossStackMode: 'guard' | 'audit';
5
+ minConfidence: number;
6
+ maxErrors?: number;
7
+ maxWarnings?: number;
8
+ strict?: false | 'inline' | 'all';
9
+ strictParse?: boolean;
10
+ description: string;
11
+ }
12
+ export interface ReviewPolicyExplicitOptions {
13
+ crossStackMode?: boolean;
14
+ minConfidence?: boolean;
15
+ maxErrors?: boolean;
16
+ maxWarnings?: boolean;
17
+ strict?: boolean;
18
+ strictParse?: boolean;
19
+ }
20
+ export declare function getReviewPolicyProfile(policy: ReviewPolicy): ReviewPolicyProfile;
21
+ export declare function inferReviewPolicy(config?: Pick<ReviewConfig, 'policy' | 'crossStackMode'>): ReviewPolicy;
22
+ export declare function applyReviewPolicyDefaults(config: ReviewConfig, explicit?: ReviewPolicyExplicitOptions): ReviewConfig;
package/dist/policy.js ADDED
@@ -0,0 +1,47 @@
1
+ const REVIEW_POLICY_PROFILES = {
2
+ guard: {
3
+ policy: 'guard',
4
+ crossStackMode: 'guard',
5
+ minConfidence: 0,
6
+ description: 'Low-noise review posture for local guardrails and PR feedback.',
7
+ },
8
+ ci: {
9
+ policy: 'ci',
10
+ crossStackMode: 'guard',
11
+ minConfidence: 0.75,
12
+ maxErrors: 0,
13
+ maxWarnings: 0,
14
+ strict: 'inline',
15
+ strictParse: true,
16
+ description: 'Strict CI posture: high-confidence findings only, no warnings by default, strict parsing.',
17
+ },
18
+ audit: {
19
+ policy: 'audit',
20
+ crossStackMode: 'audit',
21
+ minConfidence: 0,
22
+ description: 'Broad exploratory posture for local investigations and rule calibration.',
23
+ },
24
+ };
25
+ export function getReviewPolicyProfile(policy) {
26
+ return REVIEW_POLICY_PROFILES[policy];
27
+ }
28
+ export function inferReviewPolicy(config) {
29
+ if (config?.policy)
30
+ return config.policy;
31
+ return config?.crossStackMode === 'audit' ? 'audit' : 'guard';
32
+ }
33
+ export function applyReviewPolicyDefaults(config, explicit = {}) {
34
+ const policy = inferReviewPolicy(config);
35
+ const profile = getReviewPolicyProfile(policy);
36
+ return {
37
+ ...config,
38
+ policy,
39
+ crossStackMode: explicit.crossStackMode ? config.crossStackMode : profile.crossStackMode,
40
+ minConfidence: explicit.minConfidence ? config.minConfidence : profile.minConfidence,
41
+ maxErrors: explicit.maxErrors ? config.maxErrors : (profile.maxErrors ?? config.maxErrors),
42
+ maxWarnings: explicit.maxWarnings ? config.maxWarnings : (profile.maxWarnings ?? config.maxWarnings),
43
+ strict: explicit.strict ? config.strict : (profile.strict ?? config.strict),
44
+ strictParse: explicit.strictParse ? config.strictParse : (profile.strictParse ?? config.strictParse),
45
+ };
46
+ }
47
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAsBA,MAAM,sBAAsB,GAA8C;IACxE,KAAK,EAAE;QACL,MAAM,EAAE,OAAO;QACf,cAAc,EAAE,OAAO;QACvB,aAAa,EAAE,CAAC;QAChB,WAAW,EAAE,gEAAgE;KAC9E;IACD,EAAE,EAAE;QACF,MAAM,EAAE,IAAI;QACZ,cAAc,EAAE,OAAO;QACvB,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,CAAC;QACZ,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,2FAA2F;KACzG;IACD,KAAK,EAAE;QACL,MAAM,EAAE,OAAO;QACf,cAAc,EAAE,OAAO;QACvB,aAAa,EAAE,CAAC;QAChB,WAAW,EAAE,0EAA0E;KACxF;CACF,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,MAAoB;IACzD,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAwD;IACxF,IAAI,MAAM,EAAE,MAAM;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC;IACzC,OAAO,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,MAAoB,EACpB,WAAwC,EAAE;IAE1C,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAE/C,OAAO;QACL,GAAG,MAAM;QACT,MAAM;QACN,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc;QACxF,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa;QACpF,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC;QAC1F,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC;QACpG,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;QAC3E,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC;KACrG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Project-Context — repo-level signals for the review pipeline.
3
+ *
4
+ * Reads project configs (tsconfig.json, package.json) and .gitignore so rules
5
+ * can gate on what the user already enforces elsewhere. Designed to be SAFE on
6
+ * adversarial inputs:
7
+ *
8
+ * - **JSON-only.** No executable config readers (no eslint.config.js eval).
9
+ * Phase 2 may add YAML/TOML but only via safeLoad; never `require()` of a
10
+ * user-controlled file.
11
+ * - **Realpath containment.** Any path resolved from a config (extends, etc.)
12
+ * must live under the project root after `realpathSync` — otherwise it is
13
+ * ignored. Defends against `extends: '../../../etc/passwd'` and symlink
14
+ * traversal attacks surfaced by the Phase 1 red-team.
15
+ * - **Content-hash cache.** Cache key is hashed file content + extends chain
16
+ * (reuses the cache.ts pattern). mtime is unsound on second-resolution
17
+ * filesystems and on TOCTOU windows where bytes change but mtime does not.
18
+ * - **LRU eviction.** Max 128 cached project roots — defense against the
19
+ * long-running Guard bot accumulating per-PR worktree paths until OOM.
20
+ * - **Pattern-length cap on .gitignore.** Discards any individual pattern
21
+ * longer than 256 chars. Defense against ReDoS via crafted negation +
22
+ * quantifier patterns from the red-team.
23
+ */
24
+ /** What `getProjectContext` returns. Extended in later phases. */
25
+ export interface ProjectContext {
26
+ /** Absolute, realpath-resolved project root. */
27
+ root: string;
28
+ /** Parsed root package.json — restricted to fields we care about. */
29
+ packageJson?: ProjectPackageJson;
30
+ /** Parsed tsconfig (top-level only — extends chain is hashed but not deep-merged here). */
31
+ tsconfig?: ProjectTsconfig;
32
+ /** Compiled .gitignore matchers, in walk order (root first, deeper later). */
33
+ gitignore: GitignoreMatchers;
34
+ /**
35
+ * External linter configurations — used to demote kern findings that overlap
36
+ * with rules the project already enforces. Phase 2 is intentionally limited
37
+ * to JSON-only readers: `.eslintrc.json`, `package.json` `eslintConfig`, and
38
+ * `biome.json`. `eslint.config.js` is **never** evaluated (RCE risk surfaced
39
+ * by Phase 1 red-team). Per-file `overrides` resolution requires the async
40
+ * ESLint API and is left to callers via a future pre-warm path.
41
+ */
42
+ external: ExternalLinterConfig;
43
+ /**
44
+ * Set of POSIX-relative paths that `git ls-files` reports as tracked. A file
45
+ * being tracked overrides .gitignore for skip-list purposes — published
46
+ * artifacts (e.g. packages/sdk/dist/client.gen.ts) get reviewed even when
47
+ * the directory matches `.gitignore`. Empty set if not a git repo.
48
+ */
49
+ gitTrackedFiles: Set<string>;
50
+ /**
51
+ * Stable hash of every config input that contributed to this context. Used as
52
+ * the cache key; if any config file changes, the hash changes and the entry
53
+ * is recomputed.
54
+ */
55
+ contentHash: string;
56
+ }
57
+ export interface ProjectPackageJson {
58
+ name?: string;
59
+ type?: 'module' | 'commonjs';
60
+ workspaces?: string[];
61
+ bin?: Record<string, string> | string;
62
+ exports?: unknown;
63
+ private?: boolean;
64
+ }
65
+ export interface ProjectTsconfig {
66
+ /** True iff `compilerOptions.strict === true` is set on the resolved config. */
67
+ strict?: boolean;
68
+ /** Per-flag — overrides composite `strict`. Future phases dial confidence per flag. */
69
+ strictNullChecks?: boolean;
70
+ noImplicitAny?: boolean;
71
+ noUnusedLocals?: boolean;
72
+ noUnusedParameters?: boolean;
73
+ }
74
+ /** External linter rule IDs that are enabled at error/warn. */
75
+ export interface ExternalLinterConfig {
76
+ /** Rule IDs from `.eslintrc.json` / `package.json` eslintConfig that are at error or warn. */
77
+ eslintEnabledRules: Set<string>;
78
+ /** Rule IDs from `biome.json` linter.rules that are at error or warn. */
79
+ biomeEnabledRules: Set<string>;
80
+ }
81
+ /** Compiled gitignore matchers. Use `isPathIgnored` to query. */
82
+ export interface GitignoreMatchers {
83
+ /** Patterns from the project root's .gitignore, in declaration order. */
84
+ rootPatterns: GitignorePattern[];
85
+ }
86
+ export interface GitignorePattern {
87
+ /** Original line as written, post-trim. */
88
+ raw: string;
89
+ /** Compiled regex. Pattern-length capped at 256 to avoid ReDoS. */
90
+ regex: RegExp;
91
+ /** Negation rule (`!foo`) — re-includes a previously-ignored path. */
92
+ negate: boolean;
93
+ /** Pattern is anchored to repo root (no slash in middle of pattern). */
94
+ matchDirsOnly: boolean;
95
+ }
96
+ /**
97
+ * Walk up from a starting directory looking for the nearest `package.json`.
98
+ * Returns undefined if none is found before the filesystem root. Used by
99
+ * per-file review entry points that need a project context but only have a
100
+ * file path.
101
+ */
102
+ export declare function findProjectRoot(startDir: string): string | undefined;
103
+ /**
104
+ * Get the project context for a project root. Cached by content hash —
105
+ * a config file edit invalidates the entry on next call.
106
+ */
107
+ export declare function getProjectContext(projectRoot: string): ProjectContext;
108
+ /** Test-only: clear cache between tests. */
109
+ export declare function _resetProjectContextCache(): void;
110
+ /** Test-only: report current cache size. */
111
+ export declare function _projectContextCacheSize(): number;
112
+ /**
113
+ * Returns true iff the file is matched by the project's .gitignore. Use
114
+ * `isReviewable` for the full skip-list semantics — this is the gitignore
115
+ * predicate alone.
116
+ */
117
+ export declare function isPathIgnored(filePath: string, ctx: ProjectContext): boolean;
118
+ /**
119
+ * Per-flag tsconfig strictness — phase 2.3 from the red-team. The composite
120
+ * `strict: true` is shorthand for several flags; users frequently enable
121
+ * `strictNullChecks` alone without the umbrella. Rules should query the
122
+ * specific flag they depend on, not `strict`, so that `strict:false` does
123
+ * not over-debuff a finding whose underlying guarantee is in fact present.
124
+ */
125
+ export declare function isStrictFlagEffective(flag: keyof Required<ProjectTsconfig>, ctx: ProjectContext): boolean;
126
+ /**
127
+ * The full skip-list predicate. A file is reviewable iff it is NOT
128
+ * (gitignored AND not git-tracked).
129
+ *
130
+ * This is the Phase 1 red-team's finding #4 fix: a tracked artifact that lives
131
+ * inside a gitignored directory (the classic `packages/sdk/dist/client.gen.ts`
132
+ * case) must remain reviewable. Suppression-by-skip-list is reserved for
133
+ * truly-untracked outputs.
134
+ */
135
+ export declare function isReviewable(filePath: string, ctx: ProjectContext): boolean;