pumuki 6.3.294 → 6.3.295
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/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 +41 -3
- package/package.json +1 -1
|
@@ -7,10 +7,17 @@ import {
|
|
|
7
7
|
findDtoNestedPropertyWithoutNestedValidationMatch,
|
|
8
8
|
findBackendControllerRouteWithoutGuardLines,
|
|
9
9
|
findBackendControllerRouteWithoutRolesLines,
|
|
10
|
+
findBackendEventHandlerWithoutOnEventLines,
|
|
11
|
+
findBackendAuthRouteWithoutThrottleLines,
|
|
10
12
|
findBackendPermissiveCorsConfigurationLines,
|
|
11
13
|
findBackendStringLiteralUnionEnumCandidateLines,
|
|
14
|
+
findBackendSensitiveCacheWriteLines,
|
|
12
15
|
findBackendHardDeleteWithoutSoftDeleteLines,
|
|
13
16
|
findBackendLogWithoutContextLines,
|
|
17
|
+
findBackendAuthResponseWithoutRefreshTokenLines,
|
|
18
|
+
findBackendProcessEnvDefaultFallbackLines,
|
|
19
|
+
findBackendDirectProcessEnvReadLines,
|
|
20
|
+
findBackendConfigModuleWithoutValidationLines,
|
|
14
21
|
findBackendInconsistentErrorResponseLines,
|
|
15
22
|
findBackendErrorPayloadWithSuccessStatusLines,
|
|
16
23
|
findBackendControllerEntityResponseLines,
|
|
@@ -19,6 +26,7 @@ import {
|
|
|
19
26
|
findBackendRawThrowExpressionLines,
|
|
20
27
|
findBackendMissingGlobalValidationPipeLines,
|
|
21
28
|
findBackendMissingHelmetSecurityHeadersLines,
|
|
29
|
+
findBackendMissingCompressionMiddlewareLines,
|
|
22
30
|
findFrameworkDependencyImportMatch,
|
|
23
31
|
findMixedCommandQueryClassMatch,
|
|
24
32
|
findMixedCommandQueryInterfaceMatch,
|
|
@@ -27,6 +35,7 @@ import {
|
|
|
27
35
|
findNetworkCallWithoutErrorHandlingLines,
|
|
28
36
|
findNestJsConstructorDependencyWithoutDecoratorMatch,
|
|
29
37
|
findPersistenceMutationWithoutAuditEventMatch,
|
|
38
|
+
findDtoPropertyWithoutApiPropertyLines,
|
|
30
39
|
findRecordStringUnknownTypeLines,
|
|
31
40
|
findUndefinedInBaseTypeUnionLines,
|
|
32
41
|
findUnknownWithoutGuardLines,
|
|
@@ -40,11 +49,15 @@ import {
|
|
|
40
49
|
hasDefaultExportedApiRouteHandler,
|
|
41
50
|
hasDirectNetworkCall,
|
|
42
51
|
hasDtoPropertyWithoutValidation,
|
|
52
|
+
hasDtoPropertyWithoutApiProperty,
|
|
43
53
|
hasDtoNestedPropertyWithoutNestedValidation,
|
|
44
54
|
hasBackendControllerRouteWithoutGuard,
|
|
45
55
|
hasBackendControllerRouteWithoutRoles,
|
|
56
|
+
hasBackendEventHandlerWithoutOnEvent,
|
|
57
|
+
hasBackendAuthRouteWithoutThrottle,
|
|
46
58
|
hasBackendPermissiveCorsConfiguration,
|
|
47
59
|
hasBackendStringLiteralUnionEnumCandidate,
|
|
60
|
+
hasBackendSensitiveCacheWrite,
|
|
48
61
|
hasBackendHardDeleteWithoutSoftDelete,
|
|
49
62
|
hasBackendInconsistentErrorResponse,
|
|
50
63
|
hasBackendErrorPayloadWithSuccessStatus,
|
|
@@ -54,6 +67,7 @@ import {
|
|
|
54
67
|
hasBackendRawThrowExpression,
|
|
55
68
|
hasBackendMissingGlobalValidationPipe,
|
|
56
69
|
hasBackendMissingHelmetSecurityHeaders,
|
|
70
|
+
hasBackendMissingCompressionMiddleware,
|
|
57
71
|
hasEmptyCatchClause,
|
|
58
72
|
hasEvalCall,
|
|
59
73
|
hasExplicitAnyType,
|
|
@@ -64,6 +78,10 @@ import {
|
|
|
64
78
|
hasNestedIfElseStatement,
|
|
65
79
|
hasNestJsConstructorDependencyWithoutDecorator,
|
|
66
80
|
hasBackendLogWithoutContext,
|
|
81
|
+
hasBackendAuthResponseWithoutRefreshToken,
|
|
82
|
+
hasBackendProcessEnvDefaultFallback,
|
|
83
|
+
hasBackendDirectProcessEnvRead,
|
|
84
|
+
hasBackendConfigModuleWithoutValidation,
|
|
67
85
|
hasBackendMagicNumberLiteral,
|
|
68
86
|
hasBackendGenericErrorThrow,
|
|
69
87
|
hasBackendProductionMockOrSpy,
|
|
@@ -317,6 +335,237 @@ test('hasBackendLogWithoutContext detecta logs backend sin request user o trace
|
|
|
317
335
|
assert.equal(hasBackendLogWithoutContext(contextualLoggerAst), false);
|
|
318
336
|
});
|
|
319
337
|
|
|
338
|
+
test('hasBackendAuthResponseWithoutRefreshToken detecta respuestas auth solo con access token', () => {
|
|
339
|
+
const accessOnlyResponseAst = {
|
|
340
|
+
type: 'Program',
|
|
341
|
+
body: [
|
|
342
|
+
{
|
|
343
|
+
type: 'ReturnStatement',
|
|
344
|
+
argument: {
|
|
345
|
+
type: 'ObjectExpression',
|
|
346
|
+
loc: { start: { line: 16 }, end: { line: 19 } },
|
|
347
|
+
properties: [
|
|
348
|
+
{
|
|
349
|
+
type: 'ObjectProperty',
|
|
350
|
+
key: { type: 'Identifier', name: 'accessToken' },
|
|
351
|
+
value: { type: 'Identifier', name: 'accessToken' },
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
};
|
|
358
|
+
const refreshTokenResponseAst = {
|
|
359
|
+
type: 'Program',
|
|
360
|
+
body: [
|
|
361
|
+
{
|
|
362
|
+
type: 'ReturnStatement',
|
|
363
|
+
argument: {
|
|
364
|
+
type: 'ObjectExpression',
|
|
365
|
+
loc: { start: { line: 27 }, end: { line: 31 } },
|
|
366
|
+
properties: [
|
|
367
|
+
{
|
|
368
|
+
type: 'ObjectProperty',
|
|
369
|
+
key: { type: 'Identifier', name: 'accessToken' },
|
|
370
|
+
value: { type: 'Identifier', name: 'accessToken' },
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
type: 'ObjectProperty',
|
|
374
|
+
key: { type: 'Identifier', name: 'refreshToken' },
|
|
375
|
+
value: { type: 'Identifier', name: 'refreshToken' },
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
assert.equal(hasBackendAuthResponseWithoutRefreshToken(accessOnlyResponseAst), true);
|
|
384
|
+
assert.deepEqual(findBackendAuthResponseWithoutRefreshTokenLines(accessOnlyResponseAst), [16]);
|
|
385
|
+
assert.equal(hasBackendAuthResponseWithoutRefreshToken(refreshTokenResponseAst), false);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test('hasBackendProcessEnvDefaultFallback detecta defaults literales en process.env', () => {
|
|
389
|
+
const envFallbackAst = {
|
|
390
|
+
type: 'Program',
|
|
391
|
+
body: [
|
|
392
|
+
{
|
|
393
|
+
type: 'VariableDeclaration',
|
|
394
|
+
declarations: [
|
|
395
|
+
{
|
|
396
|
+
type: 'VariableDeclarator',
|
|
397
|
+
id: { type: 'Identifier', name: 'jwtSecret' },
|
|
398
|
+
init: {
|
|
399
|
+
type: 'LogicalExpression',
|
|
400
|
+
operator: '||',
|
|
401
|
+
loc: { start: { line: 6 }, end: { line: 6 } },
|
|
402
|
+
left: {
|
|
403
|
+
type: 'MemberExpression',
|
|
404
|
+
object: {
|
|
405
|
+
type: 'MemberExpression',
|
|
406
|
+
object: { type: 'Identifier', name: 'process' },
|
|
407
|
+
property: { type: 'Identifier', name: 'env' },
|
|
408
|
+
},
|
|
409
|
+
property: { type: 'Identifier', name: 'JWT_SECRET' },
|
|
410
|
+
},
|
|
411
|
+
right: { type: 'StringLiteral', value: 'dev-secret' },
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
type: 'VariableDeclarator',
|
|
416
|
+
id: { type: 'Identifier', name: 'port' },
|
|
417
|
+
init: {
|
|
418
|
+
type: 'LogicalExpression',
|
|
419
|
+
operator: '??',
|
|
420
|
+
loc: { start: { line: 9 }, end: { line: 9 } },
|
|
421
|
+
left: {
|
|
422
|
+
type: 'MemberExpression',
|
|
423
|
+
object: {
|
|
424
|
+
type: 'MemberExpression',
|
|
425
|
+
object: { type: 'Identifier', name: 'process' },
|
|
426
|
+
property: { type: 'Identifier', name: 'env' },
|
|
427
|
+
},
|
|
428
|
+
property: { type: 'Identifier', name: 'PORT' },
|
|
429
|
+
},
|
|
430
|
+
right: { type: 'NumericLiteral', value: 3000 },
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
};
|
|
437
|
+
const strictEnvAst = {
|
|
438
|
+
type: 'Program',
|
|
439
|
+
body: [
|
|
440
|
+
{
|
|
441
|
+
type: 'VariableDeclaration',
|
|
442
|
+
declarations: [
|
|
443
|
+
{
|
|
444
|
+
type: 'VariableDeclarator',
|
|
445
|
+
id: { type: 'Identifier', name: 'jwtSecret' },
|
|
446
|
+
init: {
|
|
447
|
+
type: 'CallExpression',
|
|
448
|
+
callee: { type: 'Identifier', name: 'requiredEnv' },
|
|
449
|
+
arguments: [
|
|
450
|
+
{
|
|
451
|
+
type: 'MemberExpression',
|
|
452
|
+
object: {
|
|
453
|
+
type: 'MemberExpression',
|
|
454
|
+
object: { type: 'Identifier', name: 'process' },
|
|
455
|
+
property: { type: 'Identifier', name: 'env' },
|
|
456
|
+
},
|
|
457
|
+
property: { type: 'Identifier', name: 'JWT_SECRET' },
|
|
458
|
+
},
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
},
|
|
464
|
+
],
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
assert.equal(hasBackendProcessEnvDefaultFallback(envFallbackAst), true);
|
|
468
|
+
assert.deepEqual(findBackendProcessEnvDefaultFallbackLines(envFallbackAst), [6, 9]);
|
|
469
|
+
assert.equal(hasBackendProcessEnvDefaultFallback(strictEnvAst), false);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test('hasBackendDirectProcessEnvRead detecta lecturas directas de process.env', () => {
|
|
473
|
+
const directEnvAst = {
|
|
474
|
+
type: 'Program',
|
|
475
|
+
body: [
|
|
476
|
+
{
|
|
477
|
+
type: 'VariableDeclaration',
|
|
478
|
+
declarations: [
|
|
479
|
+
{
|
|
480
|
+
type: 'VariableDeclarator',
|
|
481
|
+
id: { type: 'Identifier', name: 'databaseUrl' },
|
|
482
|
+
init: {
|
|
483
|
+
type: 'MemberExpression',
|
|
484
|
+
loc: { start: { line: 6 }, end: { line: 6 } },
|
|
485
|
+
object: {
|
|
486
|
+
type: 'MemberExpression',
|
|
487
|
+
object: { type: 'Identifier', name: 'process' },
|
|
488
|
+
property: { type: 'Identifier', name: 'env' },
|
|
489
|
+
},
|
|
490
|
+
property: { type: 'Identifier', name: 'DATABASE_URL' },
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
};
|
|
497
|
+
const configServiceAst = {
|
|
498
|
+
type: 'CallExpression',
|
|
499
|
+
callee: {
|
|
500
|
+
type: 'MemberExpression',
|
|
501
|
+
object: { type: 'Identifier', name: 'configService' },
|
|
502
|
+
property: { type: 'Identifier', name: 'getOrThrow' },
|
|
503
|
+
},
|
|
504
|
+
arguments: [{ type: 'StringLiteral', value: 'DATABASE_URL' }],
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
assert.equal(hasBackendDirectProcessEnvRead(directEnvAst), true);
|
|
508
|
+
assert.deepEqual(findBackendDirectProcessEnvReadLines(directEnvAst), [6]);
|
|
509
|
+
assert.equal(hasBackendDirectProcessEnvRead(configServiceAst), false);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
test('hasBackendConfigModuleWithoutValidation detecta ConfigModule sin validacion de env', () => {
|
|
513
|
+
const missingValidationAst = {
|
|
514
|
+
type: 'Program',
|
|
515
|
+
body: [
|
|
516
|
+
{
|
|
517
|
+
type: 'ExpressionStatement',
|
|
518
|
+
expression: {
|
|
519
|
+
type: 'CallExpression',
|
|
520
|
+
loc: { start: { line: 8 }, end: { line: 11 } },
|
|
521
|
+
callee: {
|
|
522
|
+
type: 'MemberExpression',
|
|
523
|
+
object: { type: 'Identifier', name: 'ConfigModule' },
|
|
524
|
+
property: { type: 'Identifier', name: 'forRoot' },
|
|
525
|
+
},
|
|
526
|
+
arguments: [
|
|
527
|
+
{
|
|
528
|
+
type: 'ObjectExpression',
|
|
529
|
+
properties: [
|
|
530
|
+
{
|
|
531
|
+
type: 'ObjectProperty',
|
|
532
|
+
key: { type: 'Identifier', name: 'isGlobal' },
|
|
533
|
+
value: { type: 'BooleanLiteral', value: true },
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
},
|
|
537
|
+
],
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
};
|
|
542
|
+
const validatedAst = {
|
|
543
|
+
type: 'CallExpression',
|
|
544
|
+
loc: { start: { line: 18 }, end: { line: 21 } },
|
|
545
|
+
callee: {
|
|
546
|
+
type: 'MemberExpression',
|
|
547
|
+
object: { type: 'Identifier', name: 'ConfigModule' },
|
|
548
|
+
property: { type: 'Identifier', name: 'forRoot' },
|
|
549
|
+
},
|
|
550
|
+
arguments: [
|
|
551
|
+
{
|
|
552
|
+
type: 'ObjectExpression',
|
|
553
|
+
properties: [
|
|
554
|
+
{
|
|
555
|
+
type: 'ObjectProperty',
|
|
556
|
+
key: { type: 'Identifier', name: 'validationSchema' },
|
|
557
|
+
value: { type: 'Identifier', name: 'envSchema' },
|
|
558
|
+
},
|
|
559
|
+
],
|
|
560
|
+
},
|
|
561
|
+
],
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
assert.equal(hasBackendConfigModuleWithoutValidation(missingValidationAst), true);
|
|
565
|
+
assert.deepEqual(findBackendConfigModuleWithoutValidationLines(missingValidationAst), [8]);
|
|
566
|
+
assert.equal(hasBackendConfigModuleWithoutValidation(validatedAst), false);
|
|
567
|
+
});
|
|
568
|
+
|
|
320
569
|
test('hasBackendMagicNumberLiteral detecta numeros inline y permite constantes nombradas', () => {
|
|
321
570
|
const inlineMagicNumberAst = {
|
|
322
571
|
type: 'ReturnStatement',
|
|
@@ -1094,6 +1343,55 @@ test('hasBackendStringLiteralUnionEnumCandidate detecta uniones de literales str
|
|
|
1094
1343
|
assert.equal(hasBackendStringLiteralUnionEnumCandidate(openUnionAst), false);
|
|
1095
1344
|
});
|
|
1096
1345
|
|
|
1346
|
+
test('hasBackendSensitiveCacheWrite detecta datos sensibles escritos en cache', () => {
|
|
1347
|
+
const sensitiveCacheAst = {
|
|
1348
|
+
type: 'Program',
|
|
1349
|
+
body: [
|
|
1350
|
+
{
|
|
1351
|
+
type: 'ExpressionStatement',
|
|
1352
|
+
expression: {
|
|
1353
|
+
type: 'CallExpression',
|
|
1354
|
+
loc: { start: { line: 18 }, end: { line: 18 } },
|
|
1355
|
+
callee: {
|
|
1356
|
+
type: 'MemberExpression',
|
|
1357
|
+
object: { type: 'Identifier', name: 'cacheManager' },
|
|
1358
|
+
property: { type: 'Identifier', name: 'set' },
|
|
1359
|
+
},
|
|
1360
|
+
arguments: [
|
|
1361
|
+
{ type: 'StringLiteral', value: 'session:token' },
|
|
1362
|
+
{ type: 'Identifier', name: 'accessToken' },
|
|
1363
|
+
],
|
|
1364
|
+
},
|
|
1365
|
+
},
|
|
1366
|
+
],
|
|
1367
|
+
};
|
|
1368
|
+
const safeCacheAst = {
|
|
1369
|
+
type: 'Program',
|
|
1370
|
+
body: [
|
|
1371
|
+
{
|
|
1372
|
+
type: 'ExpressionStatement',
|
|
1373
|
+
expression: {
|
|
1374
|
+
type: 'CallExpression',
|
|
1375
|
+
loc: { start: { line: 27 }, end: { line: 27 } },
|
|
1376
|
+
callee: {
|
|
1377
|
+
type: 'MemberExpression',
|
|
1378
|
+
object: { type: 'Identifier', name: 'cacheManager' },
|
|
1379
|
+
property: { type: 'Identifier', name: 'set' },
|
|
1380
|
+
},
|
|
1381
|
+
arguments: [
|
|
1382
|
+
{ type: 'StringLiteral', value: 'products:list' },
|
|
1383
|
+
{ type: 'Identifier', name: 'products' },
|
|
1384
|
+
],
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
],
|
|
1388
|
+
};
|
|
1389
|
+
|
|
1390
|
+
assert.equal(hasBackendSensitiveCacheWrite(sensitiveCacheAst), true);
|
|
1391
|
+
assert.deepEqual(findBackendSensitiveCacheWriteLines(sensitiveCacheAst), [18]);
|
|
1392
|
+
assert.equal(hasBackendSensitiveCacheWrite(safeCacheAst), false);
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1097
1395
|
test('hasBackendHardDeleteWithoutSoftDelete detecta borrados fisicos en persistencia backend', () => {
|
|
1098
1396
|
const hardDeleteAst = {
|
|
1099
1397
|
type: 'Program',
|
|
@@ -2880,6 +3178,71 @@ test('hasDtoPropertyWithoutValidation detecta DTOs sin validacion class-validato
|
|
|
2880
3178
|
assert.deepEqual(match.related_nodes, ['email']);
|
|
2881
3179
|
});
|
|
2882
3180
|
|
|
3181
|
+
test('hasDtoPropertyWithoutApiProperty detecta DTOs sin decoradores Swagger', () => {
|
|
3182
|
+
const missingSwaggerAst = {
|
|
3183
|
+
type: 'Program',
|
|
3184
|
+
body: [
|
|
3185
|
+
{
|
|
3186
|
+
type: 'ClassDeclaration',
|
|
3187
|
+
id: { type: 'Identifier', name: 'CreateOrderDto' },
|
|
3188
|
+
body: {
|
|
3189
|
+
type: 'ClassBody',
|
|
3190
|
+
body: [
|
|
3191
|
+
{
|
|
3192
|
+
type: 'ClassProperty',
|
|
3193
|
+
key: { type: 'Identifier', name: 'email' },
|
|
3194
|
+
loc: { start: { line: 7 }, end: { line: 7 } },
|
|
3195
|
+
decorators: [
|
|
3196
|
+
{
|
|
3197
|
+
type: 'Decorator',
|
|
3198
|
+
expression: {
|
|
3199
|
+
type: 'CallExpression',
|
|
3200
|
+
callee: { type: 'Identifier', name: 'IsEmail' },
|
|
3201
|
+
arguments: [],
|
|
3202
|
+
},
|
|
3203
|
+
},
|
|
3204
|
+
],
|
|
3205
|
+
},
|
|
3206
|
+
],
|
|
3207
|
+
},
|
|
3208
|
+
},
|
|
3209
|
+
],
|
|
3210
|
+
};
|
|
3211
|
+
const swaggerDtoAst = {
|
|
3212
|
+
type: 'Program',
|
|
3213
|
+
body: [
|
|
3214
|
+
{
|
|
3215
|
+
type: 'ClassDeclaration',
|
|
3216
|
+
id: { type: 'Identifier', name: 'CreateOrderDto' },
|
|
3217
|
+
body: {
|
|
3218
|
+
type: 'ClassBody',
|
|
3219
|
+
body: [
|
|
3220
|
+
{
|
|
3221
|
+
type: 'ClassProperty',
|
|
3222
|
+
key: { type: 'Identifier', name: 'email' },
|
|
3223
|
+
loc: { start: { line: 14 }, end: { line: 14 } },
|
|
3224
|
+
decorators: [
|
|
3225
|
+
{
|
|
3226
|
+
type: 'Decorator',
|
|
3227
|
+
expression: {
|
|
3228
|
+
type: 'CallExpression',
|
|
3229
|
+
callee: { type: 'Identifier', name: 'ApiProperty' },
|
|
3230
|
+
arguments: [],
|
|
3231
|
+
},
|
|
3232
|
+
},
|
|
3233
|
+
],
|
|
3234
|
+
},
|
|
3235
|
+
],
|
|
3236
|
+
},
|
|
3237
|
+
},
|
|
3238
|
+
],
|
|
3239
|
+
};
|
|
3240
|
+
|
|
3241
|
+
assert.equal(hasDtoPropertyWithoutApiProperty(missingSwaggerAst), true);
|
|
3242
|
+
assert.deepEqual(findDtoPropertyWithoutApiPropertyLines(missingSwaggerAst), [7]);
|
|
3243
|
+
assert.equal(hasDtoPropertyWithoutApiProperty(swaggerDtoAst), false);
|
|
3244
|
+
});
|
|
3245
|
+
|
|
2883
3246
|
test('hasDtoNestedPropertyWithoutNestedValidation detecta DTOs anidados sin ValidateNested y Type', () => {
|
|
2884
3247
|
const missingNestedValidationAst = {
|
|
2885
3248
|
type: 'Program',
|
|
@@ -3119,6 +3482,67 @@ test('hasBackendMissingHelmetSecurityHeaders detecta bootstrap NestJS sin Helmet
|
|
|
3119
3482
|
assert.equal(hasBackendMissingHelmetSecurityHeaders(helmetAst), false);
|
|
3120
3483
|
});
|
|
3121
3484
|
|
|
3485
|
+
test('hasBackendMissingCompressionMiddleware detecta bootstrap NestJS sin compression', () => {
|
|
3486
|
+
const missingCompressionAst = {
|
|
3487
|
+
type: 'Program',
|
|
3488
|
+
body: [
|
|
3489
|
+
{
|
|
3490
|
+
type: 'ExpressionStatement',
|
|
3491
|
+
expression: {
|
|
3492
|
+
type: 'CallExpression',
|
|
3493
|
+
loc: { start: { line: 9 }, end: { line: 9 } },
|
|
3494
|
+
callee: {
|
|
3495
|
+
type: 'MemberExpression',
|
|
3496
|
+
object: { type: 'Identifier', name: 'NestFactory' },
|
|
3497
|
+
property: { type: 'Identifier', name: 'create' },
|
|
3498
|
+
},
|
|
3499
|
+
arguments: [],
|
|
3500
|
+
},
|
|
3501
|
+
},
|
|
3502
|
+
],
|
|
3503
|
+
};
|
|
3504
|
+
const compressionAst = {
|
|
3505
|
+
type: 'Program',
|
|
3506
|
+
body: [
|
|
3507
|
+
{
|
|
3508
|
+
type: 'ExpressionStatement',
|
|
3509
|
+
expression: {
|
|
3510
|
+
type: 'CallExpression',
|
|
3511
|
+
loc: { start: { line: 7 }, end: { line: 7 } },
|
|
3512
|
+
callee: {
|
|
3513
|
+
type: 'MemberExpression',
|
|
3514
|
+
object: { type: 'Identifier', name: 'NestFactory' },
|
|
3515
|
+
property: { type: 'Identifier', name: 'create' },
|
|
3516
|
+
},
|
|
3517
|
+
arguments: [],
|
|
3518
|
+
},
|
|
3519
|
+
},
|
|
3520
|
+
{
|
|
3521
|
+
type: 'ExpressionStatement',
|
|
3522
|
+
expression: {
|
|
3523
|
+
type: 'CallExpression',
|
|
3524
|
+
callee: {
|
|
3525
|
+
type: 'MemberExpression',
|
|
3526
|
+
object: { type: 'Identifier', name: 'app' },
|
|
3527
|
+
property: { type: 'Identifier', name: 'use' },
|
|
3528
|
+
},
|
|
3529
|
+
arguments: [
|
|
3530
|
+
{
|
|
3531
|
+
type: 'CallExpression',
|
|
3532
|
+
callee: { type: 'Identifier', name: 'compression' },
|
|
3533
|
+
arguments: [],
|
|
3534
|
+
},
|
|
3535
|
+
],
|
|
3536
|
+
},
|
|
3537
|
+
},
|
|
3538
|
+
],
|
|
3539
|
+
};
|
|
3540
|
+
|
|
3541
|
+
assert.equal(hasBackendMissingCompressionMiddleware(missingCompressionAst), true);
|
|
3542
|
+
assert.deepEqual(findBackendMissingCompressionMiddlewareLines(missingCompressionAst), [9]);
|
|
3543
|
+
assert.equal(hasBackendMissingCompressionMiddleware(compressionAst), false);
|
|
3544
|
+
});
|
|
3545
|
+
|
|
3122
3546
|
test('hasBackendControllerRouteWithoutGuard detecta rutas NestJS sin UseGuards', () => {
|
|
3123
3547
|
const unguardedRouteAst = {
|
|
3124
3548
|
type: 'Program',
|
|
@@ -3190,6 +3614,85 @@ test('hasBackendControllerRouteWithoutGuard detecta rutas NestJS sin UseGuards',
|
|
|
3190
3614
|
assert.deepEqual(findBackendControllerRouteWithoutGuardLines(unguardedRouteAst), [12]);
|
|
3191
3615
|
});
|
|
3192
3616
|
|
|
3617
|
+
test('hasBackendAuthRouteWithoutThrottle detecta endpoints auth sin rate limiting', () => {
|
|
3618
|
+
const loginWithoutThrottleAst = {
|
|
3619
|
+
type: 'Program',
|
|
3620
|
+
body: [
|
|
3621
|
+
{
|
|
3622
|
+
type: 'ClassDeclaration',
|
|
3623
|
+
decorators: [
|
|
3624
|
+
{
|
|
3625
|
+
type: 'Decorator',
|
|
3626
|
+
expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'Controller' }, arguments: [] },
|
|
3627
|
+
},
|
|
3628
|
+
],
|
|
3629
|
+
body: {
|
|
3630
|
+
type: 'ClassBody',
|
|
3631
|
+
body: [
|
|
3632
|
+
{
|
|
3633
|
+
type: 'ClassMethod',
|
|
3634
|
+
key: { type: 'Identifier', name: 'login' },
|
|
3635
|
+
loc: { start: { line: 21 }, end: { line: 24 } },
|
|
3636
|
+
decorators: [
|
|
3637
|
+
{
|
|
3638
|
+
type: 'Decorator',
|
|
3639
|
+
expression: {
|
|
3640
|
+
type: 'CallExpression',
|
|
3641
|
+
callee: { type: 'Identifier', name: 'Post' },
|
|
3642
|
+
arguments: [{ type: 'StringLiteral', value: 'login' }],
|
|
3643
|
+
},
|
|
3644
|
+
},
|
|
3645
|
+
],
|
|
3646
|
+
},
|
|
3647
|
+
],
|
|
3648
|
+
},
|
|
3649
|
+
},
|
|
3650
|
+
],
|
|
3651
|
+
};
|
|
3652
|
+
const classThrottledLoginAst = {
|
|
3653
|
+
type: 'Program',
|
|
3654
|
+
body: [
|
|
3655
|
+
{
|
|
3656
|
+
type: 'ClassDeclaration',
|
|
3657
|
+
decorators: [
|
|
3658
|
+
{
|
|
3659
|
+
type: 'Decorator',
|
|
3660
|
+
expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'Controller' }, arguments: [] },
|
|
3661
|
+
},
|
|
3662
|
+
{
|
|
3663
|
+
type: 'Decorator',
|
|
3664
|
+
expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'Throttle' }, arguments: [] },
|
|
3665
|
+
},
|
|
3666
|
+
],
|
|
3667
|
+
body: {
|
|
3668
|
+
type: 'ClassBody',
|
|
3669
|
+
body: [
|
|
3670
|
+
{
|
|
3671
|
+
type: 'ClassMethod',
|
|
3672
|
+
key: { type: 'Identifier', name: 'login' },
|
|
3673
|
+
loc: { start: { line: 34 }, end: { line: 37 } },
|
|
3674
|
+
decorators: [
|
|
3675
|
+
{
|
|
3676
|
+
type: 'Decorator',
|
|
3677
|
+
expression: {
|
|
3678
|
+
type: 'CallExpression',
|
|
3679
|
+
callee: { type: 'Identifier', name: 'Post' },
|
|
3680
|
+
arguments: [{ type: 'StringLiteral', value: 'login' }],
|
|
3681
|
+
},
|
|
3682
|
+
},
|
|
3683
|
+
],
|
|
3684
|
+
},
|
|
3685
|
+
],
|
|
3686
|
+
},
|
|
3687
|
+
},
|
|
3688
|
+
],
|
|
3689
|
+
};
|
|
3690
|
+
|
|
3691
|
+
assert.equal(hasBackendAuthRouteWithoutThrottle(loginWithoutThrottleAst), true);
|
|
3692
|
+
assert.deepEqual(findBackendAuthRouteWithoutThrottleLines(loginWithoutThrottleAst), [21]);
|
|
3693
|
+
assert.equal(hasBackendAuthRouteWithoutThrottle(classThrottledLoginAst), false);
|
|
3694
|
+
});
|
|
3695
|
+
|
|
3193
3696
|
test('hasBackendControllerRouteWithoutRoles detecta rutas NestJS protegidas sin RBAC explicito', () => {
|
|
3194
3697
|
const guardedWithoutRolesAst = {
|
|
3195
3698
|
type: 'Program',
|
|
@@ -3280,3 +3783,69 @@ test('hasBackendControllerRouteWithoutRoles detecta rutas NestJS protegidas sin
|
|
|
3280
3783
|
assert.equal(hasBackendControllerRouteWithoutRoles(methodRolesAst), false);
|
|
3281
3784
|
assert.equal(hasBackendControllerRouteWithoutRoles(unguardedRouteAst), false);
|
|
3282
3785
|
});
|
|
3786
|
+
|
|
3787
|
+
test('hasBackendEventHandlerWithoutOnEvent detecta handlers backend sin decorador OnEvent', () => {
|
|
3788
|
+
const missingOnEventAst = {
|
|
3789
|
+
type: 'Program',
|
|
3790
|
+
body: [
|
|
3791
|
+
{
|
|
3792
|
+
type: 'ClassDeclaration',
|
|
3793
|
+
id: { type: 'Identifier', name: 'OrderCreatedEventHandler' },
|
|
3794
|
+
loc: { start: { line: 9 }, end: { line: 20 } },
|
|
3795
|
+
decorators: [
|
|
3796
|
+
{
|
|
3797
|
+
type: 'Decorator',
|
|
3798
|
+
expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'Injectable' }, arguments: [] },
|
|
3799
|
+
},
|
|
3800
|
+
],
|
|
3801
|
+
body: {
|
|
3802
|
+
type: 'ClassBody',
|
|
3803
|
+
body: [
|
|
3804
|
+
{
|
|
3805
|
+
type: 'ClassMethod',
|
|
3806
|
+
key: { type: 'Identifier', name: 'handleOrderCreatedEvent' },
|
|
3807
|
+
loc: { start: { line: 14 }, end: { line: 18 } },
|
|
3808
|
+
decorators: [],
|
|
3809
|
+
},
|
|
3810
|
+
],
|
|
3811
|
+
},
|
|
3812
|
+
},
|
|
3813
|
+
],
|
|
3814
|
+
};
|
|
3815
|
+
const decoratedMethodAst = {
|
|
3816
|
+
type: 'Program',
|
|
3817
|
+
body: [
|
|
3818
|
+
{
|
|
3819
|
+
type: 'ClassDeclaration',
|
|
3820
|
+
id: { type: 'Identifier', name: 'OrderCreatedEventHandler' },
|
|
3821
|
+
loc: { start: { line: 29 }, end: { line: 40 } },
|
|
3822
|
+
decorators: [
|
|
3823
|
+
{
|
|
3824
|
+
type: 'Decorator',
|
|
3825
|
+
expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'Injectable' }, arguments: [] },
|
|
3826
|
+
},
|
|
3827
|
+
],
|
|
3828
|
+
body: {
|
|
3829
|
+
type: 'ClassBody',
|
|
3830
|
+
body: [
|
|
3831
|
+
{
|
|
3832
|
+
type: 'ClassMethod',
|
|
3833
|
+
key: { type: 'Identifier', name: 'handleOrderCreatedEvent' },
|
|
3834
|
+
loc: { start: { line: 34 }, end: { line: 38 } },
|
|
3835
|
+
decorators: [
|
|
3836
|
+
{
|
|
3837
|
+
type: 'Decorator',
|
|
3838
|
+
expression: { type: 'CallExpression', callee: { type: 'Identifier', name: 'OnEvent' }, arguments: [] },
|
|
3839
|
+
},
|
|
3840
|
+
],
|
|
3841
|
+
},
|
|
3842
|
+
],
|
|
3843
|
+
},
|
|
3844
|
+
},
|
|
3845
|
+
],
|
|
3846
|
+
};
|
|
3847
|
+
|
|
3848
|
+
assert.equal(hasBackendEventHandlerWithoutOnEvent(missingOnEventAst), true);
|
|
3849
|
+
assert.deepEqual(findBackendEventHandlerWithoutOnEventLines(missingOnEventAst), [9, 14]);
|
|
3850
|
+
assert.equal(hasBackendEventHandlerWithoutOnEvent(decoratedMethodAst), false);
|
|
3851
|
+
});
|