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.
- package/CHANGELOG.md +8 -0
- package/core/facts/detectors/text/ios.test.ts +59 -0
- package/core/facts/detectors/text/ios.ts +25 -0
- package/core/facts/detectors/typescript/index.test.ts +569 -0
- package/core/facts/detectors/typescript/index.ts +312 -0
- package/core/facts/extractHeuristicFacts.ts +26 -0
- package/core/rules/presets/heuristics/ios.test.ts +11 -1
- package/core/rules/presets/heuristics/ios.ts +36 -0
- package/core/rules/presets/heuristics/typescript.test.ts +55 -1
- package/core/rules/presets/heuristics/typescript.ts +165 -0
- package/integrations/config/skillsDetectorRegistry.ts +58 -0
- package/integrations/git/runPlatformGate.ts +45 -3
- package/package.json +1 -1
|
@@ -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, 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)
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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": {
|