pumuki 6.3.270 → 6.3.271
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/CHANGELOG.md +4 -0
- package/VERSION +1 -1
- package/core/facts/detectors/text/android.test.ts +538 -0
- package/core/facts/detectors/text/android.ts +436 -0
- package/core/facts/detectors/text/ios.test.ts +328 -1
- package/core/facts/detectors/text/ios.ts +241 -0
- package/core/facts/detectors/typescript/index.test.ts +393 -0
- package/core/facts/detectors/typescript/index.ts +316 -0
- package/core/facts/extractHeuristicFacts.ts +70 -1
- package/core/rules/presets/heuristics/android.test.ts +91 -1
- package/core/rules/presets/heuristics/android.ts +360 -0
- package/core/rules/presets/heuristics/ios.test.ts +54 -1
- package/core/rules/presets/heuristics/ios.ts +243 -2
- package/core/rules/presets/heuristics/typescript.test.ts +50 -2
- package/core/rules/presets/heuristics/typescript.ts +162 -0
- package/docs/operations/RELEASE_NOTES.md +4 -0
- package/integrations/config/skillsDetectorRegistry.ts +501 -0
- package/integrations/config/skillsRuleClassification.ts +127 -3
- package/integrations/git/runPlatformGate.ts +4 -1
- package/integrations/lifecycle/preWriteAutomation.ts +1 -0
- package/integrations/lifecycle/preWriteLease.ts +41 -4
- package/package.json +1 -1
- package/scripts/classify-skills-rules.ts +2 -2
- package/scripts/framework-menu-consumer-actions-lib.ts +9 -9
- package/scripts/framework-menu-consumer-runtime-actions.ts +53 -117
- package/scripts/framework-menu-consumer-runtime-audit.ts +66 -0
- package/scripts/framework-menu-consumer-runtime-menu.ts +4 -4
- package/scripts/framework-menu-gate-lib.ts +86 -1
- package/scripts/framework-menu-layout-data.ts +3 -3
- package/scripts/framework-menu-legacy-audit-render-sections.ts +6 -0
- package/scripts/framework-menu.ts +10 -6
- package/scripts/package-install-smoke-consumer-npm-lib.ts +10 -4
- package/scripts/package-install-smoke-lifecycle-lib.ts +19 -0
|
@@ -37,6 +37,24 @@ export const iosRules: RuleSet = [
|
|
|
37
37
|
code: 'HEURISTICS_IOS_ANYVIEW_AST',
|
|
38
38
|
},
|
|
39
39
|
},
|
|
40
|
+
{
|
|
41
|
+
id: 'heuristics.ios.type-erasure.any.ast',
|
|
42
|
+
description: 'Detects Swift Any/AnyObject/AnyHashable type erasure in production code.',
|
|
43
|
+
severity: 'WARN',
|
|
44
|
+
platform: 'ios',
|
|
45
|
+
locked: true,
|
|
46
|
+
when: {
|
|
47
|
+
kind: 'Heuristic',
|
|
48
|
+
where: {
|
|
49
|
+
ruleId: 'heuristics.ios.type-erasure.any.ast',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
then: {
|
|
53
|
+
kind: 'Finding',
|
|
54
|
+
message: 'AST heuristic detected Swift Any/AnyObject/AnyHashable type erasure in production code.',
|
|
55
|
+
code: 'HEURISTICS_IOS_TYPE_ERASURE_ANY_AST',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
40
58
|
{
|
|
41
59
|
id: 'heuristics.ios.force-try.ast',
|
|
42
60
|
description: 'Detects Swift force try usage in production code.',
|
|
@@ -91,6 +109,25 @@ export const iosRules: RuleSet = [
|
|
|
91
109
|
code: 'HEURISTICS_IOS_CALLBACK_STYLE_AST',
|
|
92
110
|
},
|
|
93
111
|
},
|
|
112
|
+
{
|
|
113
|
+
id: 'heuristics.ios.combine.sink-without-store.ast',
|
|
114
|
+
description: 'Detects Combine sink subscriptions that are not retained with store(in:).',
|
|
115
|
+
severity: 'WARN',
|
|
116
|
+
platform: 'ios',
|
|
117
|
+
locked: true,
|
|
118
|
+
when: {
|
|
119
|
+
kind: 'Heuristic',
|
|
120
|
+
where: {
|
|
121
|
+
ruleId: 'heuristics.ios.combine.sink-without-store.ast',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
then: {
|
|
125
|
+
kind: 'Finding',
|
|
126
|
+
message:
|
|
127
|
+
'AST heuristic detected Combine sink without store(in:); keep cancellables retained explicitly.',
|
|
128
|
+
code: 'HEURISTICS_IOS_COMBINE_SINK_WITHOUT_STORE_AST',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
94
131
|
{
|
|
95
132
|
id: 'heuristics.ios.dispatchqueue.ast',
|
|
96
133
|
description: 'Detects DispatchQueue usage in iOS production code.',
|
|
@@ -257,6 +294,24 @@ export const iosRules: RuleSet = [
|
|
|
257
294
|
code: 'HEURISTICS_IOS_SWIFTUI_ONCHANGE_TASK_AST',
|
|
258
295
|
},
|
|
259
296
|
},
|
|
297
|
+
{
|
|
298
|
+
id: 'heuristics.ios.swiftui.onchange-readonly-var.ast',
|
|
299
|
+
description: 'Detects local var declarations inside SwiftUI onChange closures where let should be used for read-only derived values.',
|
|
300
|
+
severity: 'WARN',
|
|
301
|
+
platform: 'ios',
|
|
302
|
+
locked: true,
|
|
303
|
+
when: {
|
|
304
|
+
kind: 'Heuristic',
|
|
305
|
+
where: {
|
|
306
|
+
ruleId: 'heuristics.ios.swiftui.onchange-readonly-var.ast',
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
then: {
|
|
310
|
+
kind: 'Finding',
|
|
311
|
+
message: 'AST heuristic detected local var inside SwiftUI onChange; prefer let for read-only derived values.',
|
|
312
|
+
code: 'HEURISTICS_IOS_SWIFTUI_ONCHANGE_READONLY_VAR_AST',
|
|
313
|
+
},
|
|
314
|
+
},
|
|
260
315
|
{
|
|
261
316
|
id: 'heuristics.ios.memory.strong-delegate.ast',
|
|
262
317
|
description: 'Detects strong delegate/dataSource references in iOS production code.',
|
|
@@ -295,6 +350,63 @@ export const iosRules: RuleSet = [
|
|
|
295
350
|
code: 'HEURISTICS_IOS_MEMORY_STRONG_SELF_ESCAPING_CLOSURE_AST',
|
|
296
351
|
},
|
|
297
352
|
},
|
|
353
|
+
{
|
|
354
|
+
id: 'heuristics.ios.memory.unowned-self-capture.ast',
|
|
355
|
+
description: 'Detects unowned captures in iOS closures.',
|
|
356
|
+
severity: 'WARN',
|
|
357
|
+
platform: 'ios',
|
|
358
|
+
locked: true,
|
|
359
|
+
when: {
|
|
360
|
+
kind: 'Heuristic',
|
|
361
|
+
where: {
|
|
362
|
+
ruleId: 'heuristics.ios.memory.unowned-self-capture.ast',
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
then: {
|
|
366
|
+
kind: 'Finding',
|
|
367
|
+
message:
|
|
368
|
+
'AST heuristic detected unowned capture in an iOS closure; use weak capture unless lifetime is explicitly guaranteed.',
|
|
369
|
+
code: 'HEURISTICS_IOS_MEMORY_UNOWNED_SELF_CAPTURE_AST',
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: 'heuristics.ios.maintainability.nested-if-pyramid.ast',
|
|
374
|
+
description: 'Detects deeply nested if pyramids in iOS production code.',
|
|
375
|
+
severity: 'WARN',
|
|
376
|
+
platform: 'ios',
|
|
377
|
+
locked: true,
|
|
378
|
+
when: {
|
|
379
|
+
kind: 'Heuristic',
|
|
380
|
+
where: {
|
|
381
|
+
ruleId: 'heuristics.ios.maintainability.nested-if-pyramid.ast',
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
then: {
|
|
385
|
+
kind: 'Finding',
|
|
386
|
+
message:
|
|
387
|
+
'AST heuristic detected nested if pyramid in iOS code; prefer guard clauses and early returns.',
|
|
388
|
+
code: 'HEURISTICS_IOS_MAINTAINABILITY_NESTED_IF_PYRAMID_AST',
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
id: 'heuristics.ios.maintainability.comment-trivia.ast',
|
|
393
|
+
description: 'Detects source comments in iOS production Swift code.',
|
|
394
|
+
severity: 'WARN',
|
|
395
|
+
platform: 'ios',
|
|
396
|
+
locked: true,
|
|
397
|
+
when: {
|
|
398
|
+
kind: 'Heuristic',
|
|
399
|
+
where: {
|
|
400
|
+
ruleId: 'heuristics.ios.maintainability.comment-trivia.ast',
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
then: {
|
|
404
|
+
kind: 'Finding',
|
|
405
|
+
message:
|
|
406
|
+
'AST heuristic detected source comments in iOS production code; prefer self-documenting names and extracted concepts.',
|
|
407
|
+
code: 'HEURISTICS_IOS_MAINTAINABILITY_COMMENT_TRIVIA_AST',
|
|
408
|
+
},
|
|
409
|
+
},
|
|
298
410
|
{
|
|
299
411
|
id: 'heuristics.ios.architecture.custom-singleton.ast',
|
|
300
412
|
description: 'Detects custom static shared singletons in iOS production code.',
|
|
@@ -590,6 +702,25 @@ export const iosRules: RuleSet = [
|
|
|
590
702
|
code: 'HEURISTICS_IOS_LOCALIZATION_LOCALIZABLE_STRINGS_AST',
|
|
591
703
|
},
|
|
592
704
|
},
|
|
705
|
+
{
|
|
706
|
+
id: 'heuristics.ios.interface-builder.storyboard-xib.ast',
|
|
707
|
+
description: 'Detects Storyboard/XIB usage where programmatic UI should be used.',
|
|
708
|
+
severity: 'WARN',
|
|
709
|
+
platform: 'ios',
|
|
710
|
+
locked: true,
|
|
711
|
+
when: {
|
|
712
|
+
kind: 'Heuristic',
|
|
713
|
+
where: {
|
|
714
|
+
ruleId: 'heuristics.ios.interface-builder.storyboard-xib.ast',
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
then: {
|
|
718
|
+
kind: 'Finding',
|
|
719
|
+
message:
|
|
720
|
+
'AST heuristic detected Storyboard/XIB usage; programmatic SwiftUI/UIKit UI remains the preferred baseline.',
|
|
721
|
+
code: 'HEURISTICS_IOS_INTERFACE_BUILDER_STORYBOARD_XIB_AST',
|
|
722
|
+
},
|
|
723
|
+
},
|
|
593
724
|
{
|
|
594
725
|
id: 'heuristics.ios.localization.hardcoded-ui-string.ast',
|
|
595
726
|
description: 'Detects hardcoded user-facing SwiftUI text where String(localized:) and String Catalogs are preferred.',
|
|
@@ -698,6 +829,43 @@ export const iosRules: RuleSet = [
|
|
|
698
829
|
code: 'HEURISTICS_IOS_ACCESSIBILITY_ICON_ONLY_CONTROL_LABEL_AST',
|
|
699
830
|
},
|
|
700
831
|
},
|
|
832
|
+
{
|
|
833
|
+
id: 'heuristics.ios.accessibility.missing-accessibility-identifier.ast',
|
|
834
|
+
description: 'Detects interactive SwiftUI controls without accessibilityIdentifier.',
|
|
835
|
+
severity: 'WARN',
|
|
836
|
+
platform: 'ios',
|
|
837
|
+
locked: true,
|
|
838
|
+
when: {
|
|
839
|
+
kind: 'Heuristic',
|
|
840
|
+
where: {
|
|
841
|
+
ruleId: 'heuristics.ios.accessibility.missing-accessibility-identifier.ast',
|
|
842
|
+
},
|
|
843
|
+
},
|
|
844
|
+
then: {
|
|
845
|
+
kind: 'Finding',
|
|
846
|
+
message:
|
|
847
|
+
'AST heuristic detected an interactive SwiftUI control without accessibilityIdentifier; stable identifiers are required for UI automation and traceability.',
|
|
848
|
+
code: 'HEURISTICS_IOS_ACCESSIBILITY_MISSING_ACCESSIBILITY_IDENTIFIER_AST',
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
id: 'heuristics.ios.swiftui.missing-bindable-observable-binding.ast',
|
|
853
|
+
description: 'Detects injected @Observable values used as bindings without @Bindable.',
|
|
854
|
+
severity: 'WARN',
|
|
855
|
+
platform: 'ios',
|
|
856
|
+
locked: true,
|
|
857
|
+
when: {
|
|
858
|
+
kind: 'Heuristic',
|
|
859
|
+
where: {
|
|
860
|
+
ruleId: 'heuristics.ios.swiftui.missing-bindable-observable-binding.ast',
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
then: {
|
|
864
|
+
kind: 'Finding',
|
|
865
|
+
message: 'AST heuristic detected an injected @Observable used as a binding without @Bindable.',
|
|
866
|
+
code: 'HEURISTICS_IOS_SWIFTUI_MISSING_BINDABLE_OBSERVABLE_BINDING_AST',
|
|
867
|
+
},
|
|
868
|
+
},
|
|
701
869
|
{
|
|
702
870
|
id: 'heuristics.ios.unchecked-sendable.ast',
|
|
703
871
|
description: 'Detects @unchecked Sendable usage in iOS production code.',
|
|
@@ -788,6 +956,24 @@ export const iosRules: RuleSet = [
|
|
|
788
956
|
code: 'HEURISTICS_IOS_OBSERVABLE_OBJECT_AST',
|
|
789
957
|
},
|
|
790
958
|
},
|
|
959
|
+
{
|
|
960
|
+
id: 'heuristics.ios.swiftui.legacy-preview-provider.ast',
|
|
961
|
+
description: 'Detects PreviewProvider usage where modern SwiftUI #Preview macros should be used.',
|
|
962
|
+
severity: 'WARN',
|
|
963
|
+
platform: 'ios',
|
|
964
|
+
locked: true,
|
|
965
|
+
when: {
|
|
966
|
+
kind: 'Heuristic',
|
|
967
|
+
where: {
|
|
968
|
+
ruleId: 'heuristics.ios.swiftui.legacy-preview-provider.ast',
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
then: {
|
|
972
|
+
kind: 'Finding',
|
|
973
|
+
message: 'AST heuristic detected PreviewProvider usage; use #Preview macros for modern SwiftUI previews.',
|
|
974
|
+
code: 'HEURISTICS_IOS_SWIFTUI_LEGACY_PREVIEW_PROVIDER_AST',
|
|
975
|
+
},
|
|
976
|
+
},
|
|
791
977
|
{
|
|
792
978
|
id: 'heuristics.ios.legacy-swiftui-observable-wrapper.ast',
|
|
793
979
|
description: 'Detects @StateObject and @ObservedObject usage in modern SwiftUI production code.',
|
|
@@ -806,6 +992,61 @@ export const iosRules: RuleSet = [
|
|
|
806
992
|
code: 'HEURISTICS_IOS_LEGACY_SWIFTUI_OBSERVABLE_WRAPPER_AST',
|
|
807
993
|
},
|
|
808
994
|
},
|
|
995
|
+
{
|
|
996
|
+
id: 'heuristics.ios.testing.test-double-without-protocol.ast',
|
|
997
|
+
description: 'Detects Swift Mock/Fake/Spy/Stub classes without protocol conformance.',
|
|
998
|
+
severity: 'WARN',
|
|
999
|
+
platform: 'ios',
|
|
1000
|
+
locked: true,
|
|
1001
|
+
when: {
|
|
1002
|
+
kind: 'Heuristic',
|
|
1003
|
+
where: {
|
|
1004
|
+
ruleId: 'heuristics.ios.testing.test-double-without-protocol.ast',
|
|
1005
|
+
},
|
|
1006
|
+
},
|
|
1007
|
+
then: {
|
|
1008
|
+
kind: 'Finding',
|
|
1009
|
+
message: 'AST heuristic detected a Swift test double class without protocol conformance.',
|
|
1010
|
+
code: 'HEURISTICS_IOS_TESTING_TEST_DOUBLE_WITHOUT_PROTOCOL_AST',
|
|
1011
|
+
},
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
id: 'heuristics.ios.testing.production-test-double.ast',
|
|
1015
|
+
description: 'Detects Mock/Fake/Spy/Stub usage in iOS production code.',
|
|
1016
|
+
severity: 'WARN',
|
|
1017
|
+
platform: 'ios',
|
|
1018
|
+
locked: true,
|
|
1019
|
+
when: {
|
|
1020
|
+
kind: 'Heuristic',
|
|
1021
|
+
where: {
|
|
1022
|
+
ruleId: 'heuristics.ios.testing.production-test-double.ast',
|
|
1023
|
+
},
|
|
1024
|
+
},
|
|
1025
|
+
then: {
|
|
1026
|
+
kind: 'Finding',
|
|
1027
|
+
message: 'AST heuristic detected Mock/Fake/Spy/Stub usage in iOS production code.',
|
|
1028
|
+
code: 'HEURISTICS_IOS_TESTING_PRODUCTION_TEST_DOUBLE_AST',
|
|
1029
|
+
},
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
id: 'heuristics.ios.accessibility.low-contrast-static-color-pair.ast',
|
|
1033
|
+
description: 'Detects static SwiftUI foreground/background color pairs with insufficient contrast.',
|
|
1034
|
+
severity: 'WARN',
|
|
1035
|
+
platform: 'ios',
|
|
1036
|
+
locked: true,
|
|
1037
|
+
when: {
|
|
1038
|
+
kind: 'Heuristic',
|
|
1039
|
+
where: {
|
|
1040
|
+
ruleId: 'heuristics.ios.accessibility.low-contrast-static-color-pair.ast',
|
|
1041
|
+
},
|
|
1042
|
+
},
|
|
1043
|
+
then: {
|
|
1044
|
+
kind: 'Finding',
|
|
1045
|
+
message:
|
|
1046
|
+
'AST heuristic detected a static SwiftUI foreground/background color pair with insufficient contrast.',
|
|
1047
|
+
code: 'HEURISTICS_IOS_ACCESSIBILITY_LOW_CONTRAST_STATIC_COLOR_PAIR_AST',
|
|
1048
|
+
},
|
|
1049
|
+
},
|
|
809
1050
|
{
|
|
810
1051
|
id: 'heuristics.ios.swiftui.non-private-state-ownership.ast',
|
|
811
1052
|
description: 'Detects @State/@StateObject declarations without private visibility in SwiftUI presentation code.',
|
|
@@ -1454,7 +1695,7 @@ export const iosRules: RuleSet = [
|
|
|
1454
1695
|
},
|
|
1455
1696
|
{
|
|
1456
1697
|
id: 'heuristics.ios.testing.wait-for-expectations.ast',
|
|
1457
|
-
description: 'Detects wait(for:) and
|
|
1698
|
+
description: 'Detects wait(for:), waitForExpectations(timeout:) and waitForExistence(timeout:) usage in async iOS tests.',
|
|
1458
1699
|
severity: 'WARN',
|
|
1459
1700
|
platform: 'ios',
|
|
1460
1701
|
locked: true,
|
|
@@ -1466,7 +1707,7 @@ export const iosRules: RuleSet = [
|
|
|
1466
1707
|
},
|
|
1467
1708
|
then: {
|
|
1468
1709
|
kind: 'Finding',
|
|
1469
|
-
message: 'AST heuristic detected wait(for:)/waitForExpectations usage where
|
|
1710
|
+
message: 'AST heuristic detected wait(for:)/waitForExpectations/waitForExistence usage where an explicit async wait contract may be preferred.',
|
|
1470
1711
|
code: 'HEURISTICS_IOS_TESTING_WAIT_FOR_EXPECTATIONS_AST',
|
|
1471
1712
|
},
|
|
1472
1713
|
},
|
|
@@ -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,
|
|
6
|
+
assert.equal(typescriptRules.length, 28);
|
|
7
7
|
|
|
8
8
|
const ids = typescriptRules.map((rule) => rule.id);
|
|
9
9
|
assert.deepEqual(ids, [
|
|
@@ -16,6 +16,15 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
|
|
|
16
16
|
'heuristics.ts.set-timeout-string.ast',
|
|
17
17
|
'heuristics.ts.set-interval-string.ast',
|
|
18
18
|
'heuristics.ts.new-promise-async.ast',
|
|
19
|
+
'heuristics.ts.bcrypt-weak-salt-rounds.ast',
|
|
20
|
+
'heuristics.ts.sql-interpolated-unsafe-call.ast',
|
|
21
|
+
'heuristics.ts.sensitive-token-in-url.ast',
|
|
22
|
+
'heuristics.tsx.non-semantic-clickable.ast',
|
|
23
|
+
'heuristics.tsx.redundant-aria-role.ast',
|
|
24
|
+
'heuristics.ts.frontend-test-direct-network-call.ast',
|
|
25
|
+
'heuristics.ts.callback-hell.ast',
|
|
26
|
+
'heuristics.ts.nested-if-else.ast',
|
|
27
|
+
'heuristics.ts.next-pages-api-route.ast',
|
|
19
28
|
'heuristics.ts.with-statement.ast',
|
|
20
29
|
'heuristics.ts.delete-operator.ast',
|
|
21
30
|
'heuristics.ts.debugger.ast',
|
|
@@ -37,6 +46,42 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
|
|
|
37
46
|
byId.get('heuristics.ts.debugger.ast')?.then.code,
|
|
38
47
|
'HEURISTICS_DEBUGGER_AST'
|
|
39
48
|
);
|
|
49
|
+
assert.equal(
|
|
50
|
+
byId.get('heuristics.ts.callback-hell.ast')?.then.code,
|
|
51
|
+
'HEURISTICS_CALLBACK_HELL_AST'
|
|
52
|
+
);
|
|
53
|
+
assert.equal(
|
|
54
|
+
byId.get('heuristics.ts.nested-if-else.ast')?.then.code,
|
|
55
|
+
'HEURISTICS_NESTED_IF_ELSE_AST'
|
|
56
|
+
);
|
|
57
|
+
assert.equal(
|
|
58
|
+
byId.get('heuristics.ts.next-pages-api-route.ast')?.then.code,
|
|
59
|
+
'HEURISTICS_NEXT_PAGES_API_ROUTE_AST'
|
|
60
|
+
);
|
|
61
|
+
assert.equal(
|
|
62
|
+
byId.get('heuristics.ts.bcrypt-weak-salt-rounds.ast')?.then.code,
|
|
63
|
+
'HEURISTICS_BCRYPT_WEAK_SALT_ROUNDS_AST'
|
|
64
|
+
);
|
|
65
|
+
assert.equal(
|
|
66
|
+
byId.get('heuristics.ts.sql-interpolated-unsafe-call.ast')?.then.code,
|
|
67
|
+
'HEURISTICS_SQL_INTERPOLATED_UNSAFE_CALL_AST'
|
|
68
|
+
);
|
|
69
|
+
assert.equal(
|
|
70
|
+
byId.get('heuristics.ts.sensitive-token-in-url.ast')?.then.code,
|
|
71
|
+
'HEURISTICS_SENSITIVE_TOKEN_IN_URL_AST'
|
|
72
|
+
);
|
|
73
|
+
assert.equal(
|
|
74
|
+
byId.get('heuristics.ts.frontend-test-direct-network-call.ast')?.then.code,
|
|
75
|
+
'HEURISTICS_FRONTEND_TEST_DIRECT_NETWORK_CALL_AST'
|
|
76
|
+
);
|
|
77
|
+
assert.equal(
|
|
78
|
+
byId.get('heuristics.tsx.non-semantic-clickable.ast')?.then.code,
|
|
79
|
+
'HEURISTICS_TSX_NON_SEMANTIC_CLICKABLE_AST'
|
|
80
|
+
);
|
|
81
|
+
assert.equal(
|
|
82
|
+
byId.get('heuristics.tsx.redundant-aria-role.ast')?.then.code,
|
|
83
|
+
'HEURISTICS_TSX_REDUNDANT_ARIA_ROLE_AST'
|
|
84
|
+
);
|
|
40
85
|
assert.equal(
|
|
41
86
|
byId.get('heuristics.ts.solid.dip.framework-import.ast')?.then.code,
|
|
42
87
|
'HEURISTICS_SOLID_DIP_FRAMEWORK_IMPORT_AST'
|
|
@@ -52,7 +97,10 @@ test('typescriptRules define reglas heurísticas locked para plataforma generic'
|
|
|
52
97
|
|
|
53
98
|
for (const rule of typescriptRules) {
|
|
54
99
|
assert.equal(rule.platform, 'generic');
|
|
55
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
rule.id === 'heuristics.ts.god-class-large-class.ast' ||
|
|
102
|
+
rule.id === 'heuristics.ts.sensitive-token-in-url.ast'
|
|
103
|
+
) {
|
|
56
104
|
assert.equal(rule.severity, 'ERROR');
|
|
57
105
|
} else {
|
|
58
106
|
assert.equal(rule.severity, 'WARN');
|
|
@@ -163,6 +163,168 @@ export const typescriptRules: RuleSet = [
|
|
|
163
163
|
code: 'HEURISTICS_NEW_PROMISE_ASYNC_AST',
|
|
164
164
|
},
|
|
165
165
|
},
|
|
166
|
+
{
|
|
167
|
+
id: 'heuristics.ts.bcrypt-weak-salt-rounds.ast',
|
|
168
|
+
description: 'Detects bcrypt hashing calls with numeric salt rounds below 10.',
|
|
169
|
+
severity: 'WARN',
|
|
170
|
+
platform: 'generic',
|
|
171
|
+
locked: true,
|
|
172
|
+
when: {
|
|
173
|
+
kind: 'Heuristic',
|
|
174
|
+
where: {
|
|
175
|
+
ruleId: 'heuristics.ts.bcrypt-weak-salt-rounds.ast',
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
then: {
|
|
179
|
+
kind: 'Finding',
|
|
180
|
+
message: 'AST heuristic detected bcrypt salt rounds below 10.',
|
|
181
|
+
code: 'HEURISTICS_BCRYPT_WEAK_SALT_ROUNDS_AST',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'heuristics.ts.sql-interpolated-unsafe-call.ast',
|
|
186
|
+
description: 'Detects interpolated template SQL passed to unsafe query/raw calls.',
|
|
187
|
+
severity: 'WARN',
|
|
188
|
+
platform: 'generic',
|
|
189
|
+
locked: true,
|
|
190
|
+
when: {
|
|
191
|
+
kind: 'Heuristic',
|
|
192
|
+
where: {
|
|
193
|
+
ruleId: 'heuristics.ts.sql-interpolated-unsafe-call.ast',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
then: {
|
|
197
|
+
kind: 'Finding',
|
|
198
|
+
message: 'AST heuristic detected interpolated SQL passed to an unsafe query/raw call.',
|
|
199
|
+
code: 'HEURISTICS_SQL_INTERPOLATED_UNSAFE_CALL_AST',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 'heuristics.ts.sensitive-token-in-url.ast',
|
|
204
|
+
description: 'Detects sensitive credential query parameters in network call URLs.',
|
|
205
|
+
severity: 'ERROR',
|
|
206
|
+
platform: 'generic',
|
|
207
|
+
locked: true,
|
|
208
|
+
when: {
|
|
209
|
+
kind: 'Heuristic',
|
|
210
|
+
where: {
|
|
211
|
+
ruleId: 'heuristics.ts.sensitive-token-in-url.ast',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
then: {
|
|
215
|
+
kind: 'Finding',
|
|
216
|
+
message: 'AST heuristic detected sensitive token or credential in a network URL.',
|
|
217
|
+
code: 'HEURISTICS_SENSITIVE_TOKEN_IN_URL_AST',
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: 'heuristics.tsx.non-semantic-clickable.ast',
|
|
222
|
+
description: 'Detects non-semantic JSX elements with click handlers and no keyboard accessibility evidence.',
|
|
223
|
+
severity: 'WARN',
|
|
224
|
+
platform: 'generic',
|
|
225
|
+
locked: true,
|
|
226
|
+
when: {
|
|
227
|
+
kind: 'Heuristic',
|
|
228
|
+
where: {
|
|
229
|
+
ruleId: 'heuristics.tsx.non-semantic-clickable.ast',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
then: {
|
|
233
|
+
kind: 'Finding',
|
|
234
|
+
message: 'AST heuristic detected non-semantic clickable JSX without keyboard accessibility.',
|
|
235
|
+
code: 'HEURISTICS_TSX_NON_SEMANTIC_CLICKABLE_AST',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'heuristics.tsx.redundant-aria-role.ast',
|
|
240
|
+
description: 'Detects redundant ARIA roles on semantic JSX elements where native semantics should be used.',
|
|
241
|
+
severity: 'WARN',
|
|
242
|
+
platform: 'generic',
|
|
243
|
+
locked: true,
|
|
244
|
+
when: {
|
|
245
|
+
kind: 'Heuristic',
|
|
246
|
+
where: {
|
|
247
|
+
ruleId: 'heuristics.tsx.redundant-aria-role.ast',
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
then: {
|
|
251
|
+
kind: 'Finding',
|
|
252
|
+
message: 'AST heuristic detected redundant ARIA role on semantic JSX.',
|
|
253
|
+
code: 'HEURISTICS_TSX_REDUNDANT_ARIA_ROLE_AST',
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'heuristics.ts.frontend-test-direct-network-call.ast',
|
|
258
|
+
description: 'Detects direct fetch/axios/request calls in frontend test files where MSW should own API mocking.',
|
|
259
|
+
severity: 'WARN',
|
|
260
|
+
platform: 'generic',
|
|
261
|
+
locked: true,
|
|
262
|
+
when: {
|
|
263
|
+
kind: 'Heuristic',
|
|
264
|
+
where: {
|
|
265
|
+
ruleId: 'heuristics.ts.frontend-test-direct-network-call.ast',
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
then: {
|
|
269
|
+
kind: 'Finding',
|
|
270
|
+
message: 'AST heuristic detected direct network call usage in a frontend test; use MSW handlers instead.',
|
|
271
|
+
code: 'HEURISTICS_FRONTEND_TEST_DIRECT_NETWORK_CALL_AST',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: 'heuristics.ts.callback-hell.ast',
|
|
276
|
+
description: 'Detects nested callback functions passed to calls in TypeScript production files.',
|
|
277
|
+
severity: 'WARN',
|
|
278
|
+
platform: 'generic',
|
|
279
|
+
locked: true,
|
|
280
|
+
when: {
|
|
281
|
+
kind: 'Heuristic',
|
|
282
|
+
where: {
|
|
283
|
+
ruleId: 'heuristics.ts.callback-hell.ast',
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
then: {
|
|
287
|
+
kind: 'Finding',
|
|
288
|
+
message: 'AST heuristic detected nested callback usage.',
|
|
289
|
+
code: 'HEURISTICS_CALLBACK_HELL_AST',
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
id: 'heuristics.ts.nested-if-else.ast',
|
|
294
|
+
description: 'Detects nested if/else control flow where early returns should be preferred.',
|
|
295
|
+
severity: 'WARN',
|
|
296
|
+
platform: 'generic',
|
|
297
|
+
locked: true,
|
|
298
|
+
when: {
|
|
299
|
+
kind: 'Heuristic',
|
|
300
|
+
where: {
|
|
301
|
+
ruleId: 'heuristics.ts.nested-if-else.ast',
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
then: {
|
|
305
|
+
kind: 'Finding',
|
|
306
|
+
message: 'AST heuristic detected nested if/else control flow; prefer early returns.',
|
|
307
|
+
code: 'HEURISTICS_NESTED_IF_ELSE_AST',
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: 'heuristics.ts.next-pages-api-route.ast',
|
|
312
|
+
description: 'Detects legacy Next.js pages/api route handlers where app/api route handlers should be used.',
|
|
313
|
+
severity: 'WARN',
|
|
314
|
+
platform: 'generic',
|
|
315
|
+
locked: true,
|
|
316
|
+
when: {
|
|
317
|
+
kind: 'Heuristic',
|
|
318
|
+
where: {
|
|
319
|
+
ruleId: 'heuristics.ts.next-pages-api-route.ast',
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
then: {
|
|
323
|
+
kind: 'Finding',
|
|
324
|
+
message: 'AST heuristic detected a legacy Next.js pages/api default route handler; use app/api route handlers.',
|
|
325
|
+
code: 'HEURISTICS_NEXT_PAGES_API_ROUTE_AST',
|
|
326
|
+
},
|
|
327
|
+
},
|
|
166
328
|
{
|
|
167
329
|
id: 'heuristics.ts.with-statement.ast',
|
|
168
330
|
description: 'Detects with-statement usage in TypeScript/TSX production files.',
|
|
@@ -4,6 +4,10 @@ This file tracks the active deterministic framework line used in this repository
|
|
|
4
4
|
Canonical release chronology lives in `CHANGELOG.md`.
|
|
5
5
|
This file keeps only the operational highlights and rollout notes that matter while running the framework.
|
|
6
6
|
|
|
7
|
+
### 2026-05-18 (v6.3.271)
|
|
8
|
+
|
|
9
|
+
- Published `pumuki.3.271` with `PUMUKI-INC-142` lease parity: PRE_WRITE validated staged slices now create a `validated-diff` lease consumed by PRE_COMMIT/PRE_PUSH when the diff is unchanged, while changed slices still fail closed.
|
|
10
|
+
|
|
7
11
|
## 2026-04 (CLI stability and macOS notifications)
|
|
8
12
|
|
|
9
13
|
### 2026-05-14 (v6.3.267)
|