pumuki 6.3.294 → 6.3.296

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.
@@ -3,7 +3,7 @@ import test from 'node:test';
3
3
  import { typescriptRules } from './typescript';
4
4
 
5
5
  test('typescriptRules define reglas heurísticas locked para plataforma generic', () => {
6
- assert.equal(typescriptRules.length, 52);
6
+ assert.equal(typescriptRules.length, 61);
7
7
 
8
8
  const ids = typescriptRules.map((rule) => rule.id);
9
9
  assert.deepEqual(ids, [
@@ -50,12 +50,21 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
50
50
  'heuristics.ts.nestjs.constructor-di-without-decorator.ast',
51
51
  'heuristics.ts.backend.controller-route-without-guard.ast',
52
52
  'heuristics.ts.backend.controller-route-without-roles.ast',
53
+ 'heuristics.ts.backend.auth-route-without-throttle.ast',
54
+ 'heuristics.ts.backend.event-handler-without-on-event.ast',
53
55
  'heuristics.ts.backend.persistence-mutation-without-audit-event.ast',
56
+ 'heuristics.ts.backend.sensitive-cache-write.ast',
57
+ 'heuristics.ts.backend.auth-response-without-refresh-token.ast',
58
+ 'heuristics.ts.backend.process-env-default-fallback.ast',
59
+ 'heuristics.ts.backend.direct-process-env-read.ast',
60
+ 'heuristics.ts.backend.config-module-without-validation.ast',
54
61
  'heuristics.ts.backend.hard-delete-without-soft-delete.ast',
55
62
  'heuristics.ts.backend.dto-property-without-validation.ast',
63
+ 'heuristics.ts.backend.dto-property-without-api-property.ast',
56
64
  'heuristics.ts.backend.dto-nested-property-without-nested-validation.ast',
57
65
  'heuristics.ts.backend.missing-global-validation-pipe.ast',
58
66
  'heuristics.ts.backend.missing-helmet-security-headers.ast',
67
+ 'heuristics.ts.backend.missing-compression-middleware.ast',
59
68
  'heuristics.ts.backend.controller-business-logic.ast',
60
69
  'heuristics.ts.backend.repository-business-logic.ast',
61
70
  'heuristics.ts.god-class-large-class.ast',
@@ -178,10 +187,38 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
178
187
  byId.get('heuristics.ts.backend.controller-route-without-roles.ast')?.then.code,
179
188
  'HEURISTICS_BACKEND_CONTROLLER_ROUTE_WITHOUT_ROLES_AST'
180
189
  );
190
+ assert.equal(
191
+ byId.get('heuristics.ts.backend.auth-route-without-throttle.ast')?.then.code,
192
+ 'HEURISTICS_BACKEND_AUTH_ROUTE_WITHOUT_THROTTLE_AST'
193
+ );
194
+ assert.equal(
195
+ byId.get('heuristics.ts.backend.event-handler-without-on-event.ast')?.then.code,
196
+ 'HEURISTICS_BACKEND_EVENT_HANDLER_WITHOUT_ON_EVENT_AST'
197
+ );
181
198
  assert.equal(
182
199
  byId.get('heuristics.ts.backend.persistence-mutation-without-audit-event.ast')?.then.code,
183
200
  'HEURISTICS_BACKEND_PERSISTENCE_MUTATION_WITHOUT_AUDIT_EVENT_AST'
184
201
  );
202
+ assert.equal(
203
+ byId.get('heuristics.ts.backend.sensitive-cache-write.ast')?.then.code,
204
+ 'HEURISTICS_BACKEND_SENSITIVE_CACHE_WRITE_AST'
205
+ );
206
+ assert.equal(
207
+ byId.get('heuristics.ts.backend.auth-response-without-refresh-token.ast')?.then.code,
208
+ 'HEURISTICS_BACKEND_AUTH_RESPONSE_WITHOUT_REFRESH_TOKEN_AST'
209
+ );
210
+ assert.equal(
211
+ byId.get('heuristics.ts.backend.process-env-default-fallback.ast')?.then.code,
212
+ 'HEURISTICS_BACKEND_PROCESS_ENV_DEFAULT_FALLBACK_AST'
213
+ );
214
+ assert.equal(
215
+ byId.get('heuristics.ts.backend.direct-process-env-read.ast')?.then.code,
216
+ 'HEURISTICS_BACKEND_DIRECT_PROCESS_ENV_READ_AST'
217
+ );
218
+ assert.equal(
219
+ byId.get('heuristics.ts.backend.config-module-without-validation.ast')?.then.code,
220
+ 'HEURISTICS_BACKEND_CONFIG_MODULE_WITHOUT_VALIDATION_AST'
221
+ );
185
222
  assert.equal(
186
223
  byId.get('heuristics.ts.backend.hard-delete-without-soft-delete.ast')?.then.code,
187
224
  'HEURISTICS_BACKEND_HARD_DELETE_WITHOUT_SOFT_DELETE_AST'
@@ -190,6 +227,10 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
190
227
  byId.get('heuristics.ts.backend.dto-property-without-validation.ast')?.then.code,
191
228
  'HEURISTICS_BACKEND_DTO_PROPERTY_WITHOUT_VALIDATION_AST'
192
229
  );
230
+ assert.equal(
231
+ byId.get('heuristics.ts.backend.dto-property-without-api-property.ast')?.then.code,
232
+ 'HEURISTICS_BACKEND_DTO_PROPERTY_WITHOUT_API_PROPERTY_AST'
233
+ );
193
234
  assert.equal(
194
235
  byId.get('heuristics.ts.backend.dto-nested-property-without-nested-validation.ast')?.then.code,
195
236
  'HEURISTICS_BACKEND_DTO_NESTED_PROPERTY_WITHOUT_NESTED_VALIDATION_AST'
@@ -198,6 +239,10 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
198
239
  byId.get('heuristics.ts.backend.missing-helmet-security-headers.ast')?.then.code,
199
240
  'HEURISTICS_BACKEND_MISSING_HELMET_SECURITY_HEADERS_AST'
200
241
  );
242
+ assert.equal(
243
+ byId.get('heuristics.ts.backend.missing-compression-middleware.ast')?.then.code,
244
+ 'HEURISTICS_BACKEND_MISSING_COMPRESSION_MIDDLEWARE_AST'
245
+ );
201
246
  assert.equal(
202
247
  byId.get('heuristics.ts.god-class-large-class.ast')?.then.code,
203
248
  'HEURISTICS_GOD_CLASS_LARGE_CLASS_AST'
@@ -209,9 +254,17 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
209
254
  rule.id === 'heuristics.ts.god-class-large-class.ast' ||
210
255
  rule.id === 'heuristics.ts.backend.controller-route-without-guard.ast' ||
211
256
  rule.id === 'heuristics.ts.backend.controller-route-without-roles.ast' ||
257
+ rule.id === 'heuristics.ts.backend.auth-route-without-throttle.ast' ||
258
+ rule.id === 'heuristics.ts.backend.event-handler-without-on-event.ast' ||
212
259
  rule.id === 'heuristics.ts.backend.persistence-mutation-without-audit-event.ast' ||
260
+ rule.id === 'heuristics.ts.backend.sensitive-cache-write.ast' ||
261
+ rule.id === 'heuristics.ts.backend.auth-response-without-refresh-token.ast' ||
262
+ rule.id === 'heuristics.ts.backend.process-env-default-fallback.ast' ||
263
+ rule.id === 'heuristics.ts.backend.direct-process-env-read.ast' ||
264
+ rule.id === 'heuristics.ts.backend.config-module-without-validation.ast' ||
213
265
  rule.id === 'heuristics.ts.backend.hard-delete-without-soft-delete.ast' ||
214
266
  rule.id === 'heuristics.ts.backend.dto-property-without-validation.ast' ||
267
+ rule.id === 'heuristics.ts.backend.dto-property-without-api-property.ast' ||
215
268
  rule.id === 'heuristics.ts.backend.dto-nested-property-without-nested-validation.ast' ||
216
269
  rule.id === 'heuristics.ts.backend.sensitive-data-logging.ast' ||
217
270
  rule.id === 'heuristics.ts.backend.log-without-context.ast' ||
@@ -226,6 +279,7 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
226
279
  rule.id === 'heuristics.ts.backend.anemic-domain-model.ast' ||
227
280
  rule.id === 'heuristics.ts.backend.permissive-cors.ast' ||
228
281
  rule.id === 'heuristics.ts.backend.missing-global-validation-pipe.ast' ||
282
+ rule.id === 'heuristics.ts.backend.missing-compression-middleware.ast' ||
229
283
  rule.id === 'heuristics.ts.backend.missing-helmet-security-headers.ast' ||
230
284
  rule.id === 'heuristics.ts.backend.controller-business-logic.ast' ||
231
285
  rule.id === 'heuristics.ts.backend.repository-business-logic.ast' ||
@@ -783,6 +783,43 @@ export const typescriptRules: RuleSet = [
783
783
  code: 'HEURISTICS_BACKEND_CONTROLLER_ROUTE_WITHOUT_ROLES_AST',
784
784
  },
785
785
  },
786
+ {
787
+ id: 'heuristics.ts.backend.auth-route-without-throttle.ast',
788
+ description:
789
+ 'Detects NestJS authentication routes such as login, register, refresh or password reset without @Throttle rate limiting.',
790
+ severity: 'ERROR',
791
+ platform: 'generic',
792
+ locked: true,
793
+ when: {
794
+ kind: 'Heuristic',
795
+ where: {
796
+ ruleId: 'heuristics.ts.backend.auth-route-without-throttle.ast',
797
+ },
798
+ },
799
+ then: {
800
+ kind: 'Finding',
801
+ message: 'AST heuristic detected authentication route without rate limiting.',
802
+ code: 'HEURISTICS_BACKEND_AUTH_ROUTE_WITHOUT_THROTTLE_AST',
803
+ },
804
+ },
805
+ {
806
+ id: 'heuristics.ts.backend.event-handler-without-on-event.ast',
807
+ description: 'Detects backend event handler classes or methods without an explicit @OnEvent subscription.',
808
+ severity: 'ERROR',
809
+ platform: 'generic',
810
+ locked: true,
811
+ when: {
812
+ kind: 'Heuristic',
813
+ where: {
814
+ ruleId: 'heuristics.ts.backend.event-handler-without-on-event.ast',
815
+ },
816
+ },
817
+ then: {
818
+ kind: 'Finding',
819
+ message: 'AST heuristic detected backend event handler without @OnEvent.',
820
+ code: 'HEURISTICS_BACKEND_EVENT_HANDLER_WITHOUT_ON_EVENT_AST',
821
+ },
822
+ },
786
823
  {
787
824
  id: 'heuristics.ts.backend.persistence-mutation-without-audit-event.ast',
788
825
  description:
@@ -803,6 +840,96 @@ export const typescriptRules: RuleSet = [
803
840
  code: 'HEURISTICS_BACKEND_PERSISTENCE_MUTATION_WITHOUT_AUDIT_EVENT_AST',
804
841
  },
805
842
  },
843
+ {
844
+ id: 'heuristics.ts.backend.sensitive-cache-write.ast',
845
+ description: 'Detects backend cache writes that store tokens, passwords, secrets, email or other sensitive data.',
846
+ severity: 'ERROR',
847
+ platform: 'generic',
848
+ locked: true,
849
+ when: {
850
+ kind: 'Heuristic',
851
+ where: {
852
+ ruleId: 'heuristics.ts.backend.sensitive-cache-write.ast',
853
+ },
854
+ },
855
+ then: {
856
+ kind: 'Finding',
857
+ message: 'AST heuristic detected sensitive data written to backend cache.',
858
+ code: 'HEURISTICS_BACKEND_SENSITIVE_CACHE_WRITE_AST',
859
+ },
860
+ },
861
+ {
862
+ id: 'heuristics.ts.backend.auth-response-without-refresh-token.ast',
863
+ description: 'Detects backend auth responses that return an access token without a refresh token.',
864
+ severity: 'ERROR',
865
+ platform: 'generic',
866
+ locked: true,
867
+ when: {
868
+ kind: 'Heuristic',
869
+ where: {
870
+ ruleId: 'heuristics.ts.backend.auth-response-without-refresh-token.ast',
871
+ },
872
+ },
873
+ then: {
874
+ kind: 'Finding',
875
+ message: 'AST heuristic detected auth response without refresh token.',
876
+ code: 'HEURISTICS_BACKEND_AUTH_RESPONSE_WITHOUT_REFRESH_TOKEN_AST',
877
+ },
878
+ },
879
+ {
880
+ id: 'heuristics.ts.backend.process-env-default-fallback.ast',
881
+ description: 'Detects backend process.env configuration reads with literal production fallbacks.',
882
+ severity: 'ERROR',
883
+ platform: 'generic',
884
+ locked: true,
885
+ when: {
886
+ kind: 'Heuristic',
887
+ where: {
888
+ ruleId: 'heuristics.ts.backend.process-env-default-fallback.ast',
889
+ },
890
+ },
891
+ then: {
892
+ kind: 'Finding',
893
+ message: 'AST heuristic detected process.env fallback; fail fast when critical backend configuration is missing.',
894
+ code: 'HEURISTICS_BACKEND_PROCESS_ENV_DEFAULT_FALLBACK_AST',
895
+ },
896
+ },
897
+ {
898
+ id: 'heuristics.ts.backend.direct-process-env-read.ast',
899
+ description: 'Detects direct backend process.env reads outside the configuration boundary.',
900
+ severity: 'ERROR',
901
+ platform: 'generic',
902
+ locked: true,
903
+ when: {
904
+ kind: 'Heuristic',
905
+ where: {
906
+ ruleId: 'heuristics.ts.backend.direct-process-env-read.ast',
907
+ },
908
+ },
909
+ then: {
910
+ kind: 'Finding',
911
+ message: 'AST heuristic detected direct process.env access; use NestJS ConfigModule/ConfigService.',
912
+ code: 'HEURISTICS_BACKEND_DIRECT_PROCESS_ENV_READ_AST',
913
+ },
914
+ },
915
+ {
916
+ id: 'heuristics.ts.backend.config-module-without-validation.ast',
917
+ description: 'Detects NestJS ConfigModule.forRoot calls without environment validation.',
918
+ severity: 'ERROR',
919
+ platform: 'generic',
920
+ locked: true,
921
+ when: {
922
+ kind: 'Heuristic',
923
+ where: {
924
+ ruleId: 'heuristics.ts.backend.config-module-without-validation.ast',
925
+ },
926
+ },
927
+ then: {
928
+ kind: 'Finding',
929
+ message: 'AST heuristic detected ConfigModule.forRoot without validationSchema or validate.',
930
+ code: 'HEURISTICS_BACKEND_CONFIG_MODULE_WITHOUT_VALIDATION_AST',
931
+ },
932
+ },
806
933
  {
807
934
  id: 'heuristics.ts.backend.hard-delete-without-soft-delete.ast',
808
935
  description: 'Detects backend physical delete calls where soft delete with deletedAt/deleted_at should be used.',
@@ -841,6 +968,25 @@ export const typescriptRules: RuleSet = [
841
968
  code: 'HEURISTICS_BACKEND_DTO_PROPERTY_WITHOUT_VALIDATION_AST',
842
969
  },
843
970
  },
971
+ {
972
+ id: 'heuristics.ts.backend.dto-property-without-api-property.ast',
973
+ description:
974
+ 'Detects DTO properties without Swagger ApiProperty or ApiPropertyOptional decorators.',
975
+ severity: 'ERROR',
976
+ platform: 'generic',
977
+ locked: true,
978
+ when: {
979
+ kind: 'Heuristic',
980
+ where: {
981
+ ruleId: 'heuristics.ts.backend.dto-property-without-api-property.ast',
982
+ },
983
+ },
984
+ then: {
985
+ kind: 'Finding',
986
+ message: 'AST heuristic detected DTO property without Swagger ApiProperty decorator.',
987
+ code: 'HEURISTICS_BACKEND_DTO_PROPERTY_WITHOUT_API_PROPERTY_AST',
988
+ },
989
+ },
844
990
  {
845
991
  id: 'heuristics.ts.backend.dto-nested-property-without-nested-validation.ast',
846
992
  description: 'Detects nested DTO properties without ValidateNested/Type decorators.',
@@ -899,6 +1045,25 @@ export const typescriptRules: RuleSet = [
899
1045
  code: 'HEURISTICS_BACKEND_MISSING_HELMET_SECURITY_HEADERS_AST',
900
1046
  },
901
1047
  },
1048
+ {
1049
+ id: 'heuristics.ts.backend.missing-compression-middleware.ast',
1050
+ description:
1051
+ 'Detects NestJS bootstrap code without compression middleware for large responses.',
1052
+ severity: 'ERROR',
1053
+ platform: 'generic',
1054
+ locked: true,
1055
+ when: {
1056
+ kind: 'Heuristic',
1057
+ where: {
1058
+ ruleId: 'heuristics.ts.backend.missing-compression-middleware.ast',
1059
+ },
1060
+ },
1061
+ then: {
1062
+ kind: 'Finding',
1063
+ message: 'AST heuristic detected NestJS bootstrap without compression middleware.',
1064
+ code: 'HEURISTICS_BACKEND_MISSING_COMPRESSION_MIDDLEWARE_AST',
1065
+ },
1066
+ },
902
1067
  {
903
1068
  id: 'heuristics.ts.backend.controller-business-logic.ast',
904
1069
  description:
@@ -175,6 +175,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
175
175
  'ios.error.empty-catch',
176
176
  ['heuristics.ios.error.empty-catch.ast']
177
177
  ),
178
+ 'skills.ios.guideline.ios.error-handling-custom-networkerror-enum': heuristicDetector(
179
+ 'ios.error.typed-error-enum',
180
+ ['heuristics.ios.error.nserror-throw.ast']
181
+ ),
182
+ 'skills.ios.guideline.ios.error-handling-global-custom-error-enum': heuristicDetector(
183
+ 'ios.error.typed-error-enum',
184
+ ['heuristics.ios.error.nserror-throw.ast']
185
+ ),
178
186
  'skills.ios.guideline.ios.no-loggear-pii-tokens-emails-ids-sensibles': heuristicDetector(
179
187
  'ios.logging.sensitive-data',
180
188
  ['heuristics.ios.logging.sensitive-data.ast']
@@ -364,6 +372,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
364
372
  heuristicDetector('ios.swiftui.environment-system-values-no-swinject', [
365
373
  'heuristics.ios.architecture.swinject.ast',
366
374
  ]),
375
+ 'skills.ios.guideline.ios.dependencies-en-package-swift-versiones-especi-ficas':
376
+ heuristicDetector('ios.dependencies.swiftpm-branch-dependency', [
377
+ 'heuristics.ios.dependencies.swiftpm-branch-dependency.ast',
378
+ ]),
367
379
  'skills.ios.no-unchecked-sendable': heuristicDetector('ios.unchecked-sendable', [
368
380
  'heuristics.ios.unchecked-sendable.ast',
369
381
  ]),
@@ -397,6 +409,8 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
397
409
  'skills.ios.no-assume-isolated': heuristicDetector('ios.assume-isolated', [
398
410
  'heuristics.ios.assume-isolated.ast',
399
411
  ]),
412
+ 'skills.ios.guideline.ios.no-usar-mainactor-como-parche-justificar-el-aislamiento':
413
+ heuristicDetector('ios.assume-isolated', ['heuristics.ios.assume-isolated.ast']),
400
414
  'skills.ios.no-observable-object': heuristicDetector('ios.observable-object', [
401
415
  'heuristics.ios.observable-object.ast',
402
416
  ]),
@@ -781,6 +795,38 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
781
795
  heuristicDetector('typescript.backend.persistence-mutation-without-audit-event', [
782
796
  'heuristics.ts.backend.persistence-mutation-without-audit-event.ast',
783
797
  ]),
798
+ 'skills.backend.guideline.backend.event-handlers-onevent-order-created': heuristicDetector(
799
+ 'typescript.backend.event-handler-without-on-event',
800
+ ['heuristics.ts.backend.event-handler-without-on-event.ast']
801
+ ),
802
+ 'skills.backend.guideline.backend.no-cachear-datos-sensibles-o-cifrar-antes-de-cachear':
803
+ heuristicDetector('typescript.backend.sensitive-cache-write', [
804
+ 'heuristics.ts.backend.sensitive-cache-write.ast',
805
+ ]),
806
+ 'skills.backend.guideline.backend.refresh-tokens-no-solo-access-tokens':
807
+ heuristicDetector('typescript.backend.auth-response-without-refresh-token', [
808
+ 'heuristics.ts.backend.auth-response-without-refresh-token.ast',
809
+ ]),
810
+ 'skills.backend.guideline.backend.no-defaults-en-produccio-n-fallar-si-falta-config-cri-tica':
811
+ heuristicDetector('typescript.backend.process-env-default-fallback', [
812
+ 'heuristics.ts.backend.process-env-default-fallback.ast',
813
+ ]),
814
+ 'skills.backend.guideline.backend.correlation-ids-para-tracing-distribuido':
815
+ heuristicDetector('typescript.backend.log-without-context', [
816
+ 'heuristics.ts.backend.log-without-context.ast',
817
+ ]),
818
+ 'skills.backend.guideline.backend.nestjs-config-configmodule-para-variables-de-entorno':
819
+ heuristicDetector('typescript.backend.direct-process-env-read', [
820
+ 'heuristics.ts.backend.direct-process-env-read.ast',
821
+ ]),
822
+ 'skills.backend.guideline.backend.validation-de-config-joi-o-class-validator-para-env':
823
+ heuristicDetector('typescript.backend.config-module-without-validation', [
824
+ 'heuristics.ts.backend.config-module-without-validation.ast',
825
+ ]),
826
+ 'skills.backend.guideline.backend.compression-gzip-para-responses-grandes':
827
+ heuristicDetector('typescript.backend.missing-compression-middleware', [
828
+ 'heuristics.ts.backend.missing-compression-middleware.ast',
829
+ ]),
784
830
  'skills.backend.guideline.backend.soft-deletes-deletedat-en-lugar-de-delete-fi-sico':
785
831
  heuristicDetector('typescript.backend.hard-delete-without-soft-delete', [
786
832
  'heuristics.ts.backend.hard-delete-without-soft-delete.ast',
@@ -809,6 +855,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
809
855
  heuristicDetector('typescript.backend.dto-property-without-validation', [
810
856
  'heuristics.ts.backend.dto-property-without-validation.ast',
811
857
  ]),
858
+ 'skills.backend.guideline.backend.swagger-nestjs-swagger-decoradores-apiproperty':
859
+ heuristicDetector('typescript.backend.dto-property-without-api-property', [
860
+ 'heuristics.ts.backend.dto-property-without-api-property.ast',
861
+ ]),
812
862
  'skills.backend.guideline.backend.nested-validation-validatenested-type':
813
863
  heuristicDetector('typescript.backend.dto-nested-property-without-nested-validation', [
814
864
  'heuristics.ts.backend.dto-nested-property-without-nested-validation.ast',
@@ -837,6 +887,14 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
837
887
  heuristicDetector('typescript.backend.controller-route-without-roles', [
838
888
  'heuristics.ts.backend.controller-route-without-roles.ast',
839
889
  ]),
890
+ 'skills.backend.guideline.backend.rate-limiting-nestjs-throttler-para-prevenir-brute-force':
891
+ heuristicDetector('typescript.backend.auth-route-without-throttle', [
892
+ 'heuristics.ts.backend.auth-route-without-throttle.ast',
893
+ ]),
894
+ 'skills.backend.guideline.backend.rate-limiting-throttler-para-prevenir-abuse':
895
+ heuristicDetector('typescript.backend.auth-route-without-throttle', [
896
+ 'heuristics.ts.backend.auth-route-without-throttle.ast',
897
+ ]),
840
898
  'skills.backend.guideline.backend.callback-hell-usar-async-await': heuristicDetector(
841
899
  'typescript.callback-hell',
842
900
  ['heuristics.ts.callback-hell.ast']
@@ -196,6 +196,33 @@ const isFindingOutsideChangedLines = (
196
196
  return findingLines.every((line) => !changedLines.has(line));
197
197
  };
198
198
 
199
+ const BROAD_BROWNFIELD_FINDING_LINES_THRESHOLD = 20;
200
+
201
+ const isBroadFileLevelFinding = (finding: Finding): boolean => {
202
+ const findingLines = normalizeFindingLines(finding.lines);
203
+ if (findingLines.length <= BROAD_BROWNFIELD_FINDING_LINES_THRESHOLD) {
204
+ return false;
205
+ }
206
+ const nodeLines = [
207
+ ...normalizeFindingLines(finding.primary_node?.lines),
208
+ ...(finding.related_nodes ?? []).flatMap((node) => normalizeFindingLines(node.lines)),
209
+ ];
210
+ const hasPreciseNode =
211
+ nodeLines.length > 0 && nodeLines.length <= BROAD_BROWNFIELD_FINDING_LINES_THRESHOLD;
212
+ return !hasPreciseNode;
213
+ };
214
+
215
+ const isFindingTooBroadForStagedDiffBlock = (
216
+ finding: Finding,
217
+ changedLinesByPath: ReadonlyMap<string, ReadonlySet<number>>
218
+ ): boolean => {
219
+ if (!finding.filePath || !isBroadFileLevelFinding(finding)) {
220
+ return false;
221
+ }
222
+ const changedLines = changedLinesByPath.get(toNormalizedPath(finding.filePath));
223
+ return typeof changedLines !== 'undefined' && changedLines.size > 0;
224
+ };
225
+
199
226
  const normalizeScopedRuleEngineFindings = (params: {
200
227
  findings: ReadonlyArray<Finding>;
201
228
  scope: GateScope;
@@ -210,7 +237,18 @@ const normalizeScopedRuleEngineFindings = (params: {
210
237
  }
211
238
 
212
239
  return params.findings.map((finding) => {
213
- if (!isAstRuleFinding(finding) || !isFindingOutsideChangedLines(finding, params.changedLinesByPath)) {
240
+ if (!isAstRuleFinding(finding)) {
241
+ return finding;
242
+ }
243
+ const outsideChangedLines = isFindingOutsideChangedLines(
244
+ finding,
245
+ params.changedLinesByPath
246
+ );
247
+ const tooBroadForDiffBlock = isFindingTooBroadForStagedDiffBlock(
248
+ finding,
249
+ params.changedLinesByPath
250
+ );
251
+ if (!outsideChangedLines && !tooBroadForDiffBlock) {
214
252
  return finding;
215
253
  }
216
254
  return {
@@ -219,10 +257,14 @@ const normalizeScopedRuleEngineFindings = (params: {
219
257
  severity: 'INFO',
220
258
  message:
221
259
  `${finding.message} ` +
222
- 'Baseline brownfield outside the staged diff; tracked as advisory for this atomic slice.',
260
+ (tooBroadForDiffBlock
261
+ ? 'Finding is file-level/broad and cannot prove the violation was introduced by the staged diff; tracked as advisory for this atomic slice.'
262
+ : 'Baseline brownfield outside the staged diff; tracked as advisory for this atomic slice.'),
223
263
  expected_fix:
224
264
  finding.expected_fix ??
225
- 'Plan a dedicated brownfield remediation slice for this pre-existing finding.',
265
+ (tooBroadForDiffBlock
266
+ ? 'Emit a precise AST/nodal finding with line-level evidence for the changed node, or plan a dedicated brownfield remediation slice for this file-level debt.'
267
+ : 'Plan a dedicated brownfield remediation slice for this pre-existing finding.'),
226
268
  };
227
269
  });
228
270
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.294",
3
+ "version": "6.3.296",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {