@0xcraft/powershot 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -9
- package/dist/cli/args.js +2 -1
- package/dist/cli/review-command.js +30 -12
- package/dist/cli/session-command.js +1 -0
- package/dist/delegate.js +89 -13
- package/dist/git.js +5 -3
- package/dist/ground.js +19 -12
- package/dist/lang/packs.js +170 -8
- package/dist/langtest.js +1056 -6
- package/dist/plan.js +25 -0
- package/dist/reinvention.js +227 -2
- package/dist/report/sarif.js +2 -2
- package/dist/report/summary.js +12 -4
- package/dist/review.js +5 -13
- package/dist/selftest.js +507 -17
- package/dist/session.js +1 -0
- package/dist/verifiers/dropped-guard.js +188 -84
- package/dist/verifiers/foreign-dropped-guard.js +174 -50
- package/dist/verifiers/foreign-reinvented.js +22 -7
- package/dist/verifiers/foreign-tokens.js +228 -19
- package/dist/verifiers/guard-diff.js +138 -0
- package/dist/verifiers/reinvented.js +7 -5
- package/docs/architecture.md +33 -0
- package/docs/ci.md +9 -4
- package/package.json +1 -1
package/dist/lang/packs.js
CHANGED
|
@@ -5,11 +5,29 @@ import { Worker } from 'node:worker_threads';
|
|
|
5
5
|
const COMMON_NODES = {
|
|
6
6
|
identifier: ['identifier'],
|
|
7
7
|
comment: ['comment', 'line_comment', 'block_comment'],
|
|
8
|
-
ifCondition: 'condition',
|
|
9
|
-
ifBody: 'consequence',
|
|
8
|
+
ifCondition: ['condition'],
|
|
9
|
+
ifBody: ['consequence', 'body'],
|
|
10
|
+
ifAlternative: ['alternative'],
|
|
10
11
|
declarationName: 'name',
|
|
11
12
|
block: ['block'],
|
|
13
|
+
callableBody: ['block'],
|
|
14
|
+
callableOwner: [],
|
|
15
|
+
callableOwnerBody: [],
|
|
16
|
+
fileScope: [],
|
|
17
|
+
reusableContainer: [],
|
|
18
|
+
reusableScope: [],
|
|
19
|
+
reusableWrapper: [],
|
|
20
|
+
reusablePrefix: [],
|
|
21
|
+
reusableBlocker: [],
|
|
22
|
+
bindingContext: [],
|
|
23
|
+
bindingDeclaration: [],
|
|
24
|
+
bindingIdentifier: ['identifier'],
|
|
12
25
|
};
|
|
26
|
+
function declarationHeader(node) {
|
|
27
|
+
const boundaries = [node.text.indexOf('{')];
|
|
28
|
+
const end = boundaries.filter((index) => index >= 0).sort((left, right) => left - right)[0];
|
|
29
|
+
return end === undefined ? node.text : node.text.slice(0, end);
|
|
30
|
+
}
|
|
13
31
|
/** Pull the key out of `getenv("HOME")` / `environ["HOME"]` style reads. */
|
|
14
32
|
function envKeysFrom(root, callPattern, callTypes) {
|
|
15
33
|
const out = [];
|
|
@@ -106,9 +124,16 @@ const PYTHON = {
|
|
|
106
124
|
nodes: {
|
|
107
125
|
...COMMON_NODES,
|
|
108
126
|
ifStatement: ['if_statement'],
|
|
109
|
-
ifBody: 'consequence',
|
|
110
127
|
bail: ['return_statement', 'raise_statement', 'continue_statement', 'break_statement'],
|
|
111
128
|
declaration: ['function_definition', 'class_definition'],
|
|
129
|
+
callable: ['function_definition'],
|
|
130
|
+
callableOwner: ['class_definition', 'decorated_definition'],
|
|
131
|
+
callableOwnerBody: ['block', 'function_definition', 'class_definition'],
|
|
132
|
+
reusableDeclaration: ['function_definition', 'class_definition'],
|
|
133
|
+
reusableContainer: ['decorated_definition'],
|
|
134
|
+
reusableWrapper: ['decorated_definition'],
|
|
135
|
+
bindingContext: ['import_statement', 'import_from_statement'],
|
|
136
|
+
bindingDeclaration: ['expression_statement'],
|
|
112
137
|
},
|
|
113
138
|
envReads(root) {
|
|
114
139
|
return envKeysFrom(root, /os\.environ|getenv/, ['call', 'subscript']);
|
|
@@ -251,6 +276,12 @@ const PYTHON = {
|
|
|
251
276
|
}
|
|
252
277
|
return out;
|
|
253
278
|
},
|
|
279
|
+
fileConstraints(root) {
|
|
280
|
+
return root.namedChildren
|
|
281
|
+
.filter((node) => COMMON_NODES.comment.includes(node.type) && node.startPosition.row <= 1)
|
|
282
|
+
.map((node) => node.text.trim())
|
|
283
|
+
.filter((text) => /coding\s*[:=]/.test(text));
|
|
284
|
+
},
|
|
254
285
|
};
|
|
255
286
|
const GO = {
|
|
256
287
|
name: 'go',
|
|
@@ -261,6 +292,12 @@ const GO = {
|
|
|
261
292
|
ifStatement: ['if_statement'],
|
|
262
293
|
bail: ['return_statement', 'continue_statement', 'break_statement', 'goto_statement'],
|
|
263
294
|
declaration: ['function_declaration', 'method_declaration', 'type_declaration'],
|
|
295
|
+
callable: ['function_declaration', 'method_declaration'],
|
|
296
|
+
fileScope: ['package_clause'],
|
|
297
|
+
reusableDeclaration: ['function_declaration', 'type_declaration'],
|
|
298
|
+
bindingContext: ['import_declaration'],
|
|
299
|
+
bindingDeclaration: ['const_declaration', 'var_declaration', 'type_declaration'],
|
|
300
|
+
bindingIdentifier: ['identifier', 'type_identifier'],
|
|
264
301
|
},
|
|
265
302
|
envReads(root) {
|
|
266
303
|
return envKeysFrom(root, /os\.Getenv|os\.LookupEnv/, ['call_expression']);
|
|
@@ -281,6 +318,12 @@ const GO = {
|
|
|
281
318
|
// the blank identifier is someone saying "I know". An empty `if err != nil` isn't.
|
|
282
319
|
return out;
|
|
283
320
|
},
|
|
321
|
+
fileConstraints(root) {
|
|
322
|
+
return root.namedChildren
|
|
323
|
+
.filter((node) => COMMON_NODES.comment.includes(node.type))
|
|
324
|
+
.map((node) => node.text.trim())
|
|
325
|
+
.filter((text) => /^\/\/go:build\b|^\/\/\s*\+build\b/.test(text));
|
|
326
|
+
},
|
|
284
327
|
};
|
|
285
328
|
const JAVA = {
|
|
286
329
|
name: 'java',
|
|
@@ -291,6 +334,17 @@ const JAVA = {
|
|
|
291
334
|
ifStatement: ['if_statement'],
|
|
292
335
|
bail: ['return_statement', 'throw_statement', 'continue_statement', 'break_statement'],
|
|
293
336
|
declaration: ['method_declaration', 'class_declaration', 'interface_declaration', 'record_declaration'],
|
|
337
|
+
callable: ['method_declaration', 'constructor_declaration'],
|
|
338
|
+
fileScope: ['package_declaration'],
|
|
339
|
+
callableBody: ['block', 'constructor_body'],
|
|
340
|
+
callableOwner: [
|
|
341
|
+
'class_declaration', 'interface_declaration', 'record_declaration',
|
|
342
|
+
'enum_declaration', 'annotation_type_declaration',
|
|
343
|
+
],
|
|
344
|
+
callableOwnerBody: ['class_body', 'interface_body', 'record_body', 'enum_body', 'annotation_type_body'],
|
|
345
|
+
reusableDeclaration: ['class_declaration', 'interface_declaration', 'record_declaration'],
|
|
346
|
+
bindingContext: ['import_declaration'],
|
|
347
|
+
block: ['block', 'constructor_body'],
|
|
294
348
|
},
|
|
295
349
|
envReads(root) {
|
|
296
350
|
return envKeysFrom(root, /System\.getenv/, ['method_invocation']);
|
|
@@ -304,10 +358,16 @@ const RUST = {
|
|
|
304
358
|
nodes: {
|
|
305
359
|
...COMMON_NODES,
|
|
306
360
|
ifStatement: ['if_expression'],
|
|
307
|
-
ifCondition: 'condition',
|
|
308
|
-
ifBody: 'consequence',
|
|
309
361
|
bail: ['return_expression', 'break_expression', 'continue_expression'],
|
|
310
362
|
declaration: ['function_item', 'struct_item', 'enum_item', 'trait_item'],
|
|
363
|
+
callable: ['function_item'],
|
|
364
|
+
callableOwner: ['impl_item', 'trait_item', 'mod_item'],
|
|
365
|
+
callableOwnerBody: ['declaration_list'],
|
|
366
|
+
reusableDeclaration: ['function_item', 'struct_item', 'enum_item', 'trait_item'],
|
|
367
|
+
reusablePrefix: ['attribute_item'],
|
|
368
|
+
bindingContext: ['use_declaration', 'extern_crate_declaration'],
|
|
369
|
+
bindingDeclaration: ['const_item', 'static_item', 'type_item', 'mod_item'],
|
|
370
|
+
bindingIdentifier: ['identifier', 'type_identifier'],
|
|
311
371
|
},
|
|
312
372
|
envReads(root) {
|
|
313
373
|
return envKeysFrom(root, /env::var|env::var_os/, ['call_expression']);
|
|
@@ -328,6 +388,14 @@ const RUST = {
|
|
|
328
388
|
}
|
|
329
389
|
return out;
|
|
330
390
|
},
|
|
391
|
+
reusableAcrossFiles(node) {
|
|
392
|
+
// `pub(self)`, `pub(super)`, and `pub(in ...)` need a module graph to prove
|
|
393
|
+
// accessibility from another file. Plain `pub` and crate-wide visibility do not.
|
|
394
|
+
return /\bpub(?:\s*\(\s*crate\s*\))?\s+/.test(declarationHeader(node));
|
|
395
|
+
},
|
|
396
|
+
fileConstraints(root) {
|
|
397
|
+
return nodesOfType(root, ['inner_attribute_item']).map((node) => node.text.trim());
|
|
398
|
+
},
|
|
331
399
|
};
|
|
332
400
|
const CLIKE_LOG = /^(std::(cout|cerr)|printf|fprintf|Console\.|Log|log|logger|error_log|print_r|var_dump)/;
|
|
333
401
|
const CPP = {
|
|
@@ -339,11 +407,30 @@ const CPP = {
|
|
|
339
407
|
ifStatement: ['if_statement'],
|
|
340
408
|
bail: ['return_statement', 'throw_statement', 'break_statement', 'continue_statement', 'goto_statement'],
|
|
341
409
|
declaration: ['function_definition'],
|
|
410
|
+
callable: ['function_definition'],
|
|
411
|
+
callableBody: ['compound_statement'],
|
|
412
|
+
callableOwner: [
|
|
413
|
+
'namespace_definition', 'class_specifier', 'struct_specifier', 'union_specifier', 'template_declaration',
|
|
414
|
+
],
|
|
415
|
+
callableOwnerBody: ['declaration_list', 'field_declaration_list', 'function_definition'],
|
|
416
|
+
reusableDeclaration: ['function_definition'],
|
|
417
|
+
reusableContainer: ['namespace_definition', 'declaration_list', 'template_declaration'],
|
|
418
|
+
reusableScope: ['namespace_definition'],
|
|
419
|
+
reusableWrapper: ['template_declaration'],
|
|
420
|
+
bindingContext: [
|
|
421
|
+
'preproc_include', 'preproc_def', 'preproc_function_def',
|
|
422
|
+
'using_declaration', 'alias_declaration', 'namespace_alias_definition',
|
|
423
|
+
],
|
|
424
|
+
bindingDeclaration: ['declaration', 'type_definition'],
|
|
425
|
+
bindingIdentifier: ['identifier', 'type_identifier', 'namespace_identifier'],
|
|
342
426
|
declarationName: 'declarator',
|
|
343
427
|
block: ['compound_statement'],
|
|
344
428
|
},
|
|
345
429
|
envReads: (root) => envKeysFrom(root, /getenv|GetEnvironmentVariable/, ['call_expression']),
|
|
346
430
|
swallowedError: catchBased(['catch_clause'], 'body', CLIKE_LOG),
|
|
431
|
+
reusableAcrossFiles(node) {
|
|
432
|
+
return !/\bstatic\b/.test(declarationHeader(node));
|
|
433
|
+
},
|
|
347
434
|
};
|
|
348
435
|
const C = {
|
|
349
436
|
name: 'c',
|
|
@@ -354,10 +441,19 @@ const C = {
|
|
|
354
441
|
ifStatement: ['if_statement'],
|
|
355
442
|
bail: ['return_statement', 'break_statement', 'continue_statement', 'goto_statement'],
|
|
356
443
|
declaration: ['function_definition'],
|
|
444
|
+
callable: ['function_definition'],
|
|
445
|
+
callableBody: ['compound_statement'],
|
|
446
|
+
reusableDeclaration: ['function_definition'],
|
|
447
|
+
bindingContext: ['preproc_include', 'preproc_def', 'preproc_function_def'],
|
|
448
|
+
bindingDeclaration: ['declaration', 'type_definition'],
|
|
449
|
+
bindingIdentifier: ['identifier', 'type_identifier'],
|
|
357
450
|
declarationName: 'declarator',
|
|
358
451
|
block: ['compound_statement'],
|
|
359
452
|
},
|
|
360
453
|
envReads: (root) => envKeysFrom(root, /getenv/, ['call_expression']),
|
|
454
|
+
reusableAcrossFiles(node) {
|
|
455
|
+
return !/\bstatic\b/.test(declarationHeader(node));
|
|
456
|
+
},
|
|
361
457
|
// C has no exceptions; its error handling is return codes, which cannot be told
|
|
362
458
|
// from ordinary control flow without types. The other checks still apply.
|
|
363
459
|
};
|
|
@@ -370,6 +466,17 @@ const CSHARP = {
|
|
|
370
466
|
ifStatement: ['if_statement'],
|
|
371
467
|
bail: ['return_statement', 'throw_statement', 'break_statement', 'continue_statement'],
|
|
372
468
|
declaration: ['method_declaration', 'class_declaration', 'interface_declaration', 'record_declaration'],
|
|
469
|
+
callable: ['method_declaration', 'constructor_declaration', 'local_function_statement'],
|
|
470
|
+
callableOwner: [
|
|
471
|
+
'namespace_declaration', 'file_scoped_namespace_declaration',
|
|
472
|
+
'class_declaration', 'struct_declaration', 'interface_declaration', 'record_declaration',
|
|
473
|
+
],
|
|
474
|
+
callableOwnerBody: ['declaration_list'],
|
|
475
|
+
reusableDeclaration: ['class_declaration', 'interface_declaration', 'record_declaration'],
|
|
476
|
+
reusableContainer: ['namespace_declaration', 'file_scoped_namespace_declaration', 'declaration_list'],
|
|
477
|
+
reusableScope: ['namespace_declaration', 'file_scoped_namespace_declaration'],
|
|
478
|
+
reusableBlocker: ['ERROR'],
|
|
479
|
+
bindingContext: ['using_directive', 'extern_alias_directive'],
|
|
373
480
|
block: ['block', 'declaration_list'],
|
|
374
481
|
},
|
|
375
482
|
envReads: (root) => envKeysFrom(root, /GetEnvironmentVariable/, ['invocation_expression']),
|
|
@@ -384,6 +491,18 @@ const PHP = {
|
|
|
384
491
|
ifStatement: ['if_statement'],
|
|
385
492
|
bail: ['return_statement', 'throw_expression', 'break_statement', 'continue_statement'],
|
|
386
493
|
declaration: ['function_definition', 'method_declaration', 'class_declaration'],
|
|
494
|
+
callable: ['function_definition', 'method_declaration'],
|
|
495
|
+
callableBody: ['compound_statement'],
|
|
496
|
+
callableOwner: [
|
|
497
|
+
'namespace_definition', 'class_declaration', 'interface_declaration', 'trait_declaration', 'enum_declaration',
|
|
498
|
+
],
|
|
499
|
+
callableOwnerBody: ['compound_statement', 'declaration_list'],
|
|
500
|
+
reusableDeclaration: ['function_definition', 'class_declaration'],
|
|
501
|
+
reusableContainer: ['namespace_definition', 'compound_statement'],
|
|
502
|
+
reusableScope: ['namespace_definition'],
|
|
503
|
+
bindingContext: ['namespace_use_declaration', 'expression_statement'],
|
|
504
|
+
bindingDeclaration: ['const_declaration'],
|
|
505
|
+
bindingIdentifier: ['name', 'variable_name'],
|
|
387
506
|
block: ['compound_statement'],
|
|
388
507
|
},
|
|
389
508
|
envReads: (root) => envKeysFrom(root, /getenv|\$_ENV/, ['function_call_expression', 'subscript_expression']),
|
|
@@ -399,10 +518,23 @@ const KOTLIN = {
|
|
|
399
518
|
ifStatement: ['if_expression'],
|
|
400
519
|
bail: ['jump_expression'],
|
|
401
520
|
declaration: ['function_declaration', 'class_declaration', 'object_declaration'],
|
|
521
|
+
callable: ['function_declaration'],
|
|
522
|
+
fileScope: ['package_header'],
|
|
523
|
+
callableBody: ['function_body', 'block', 'statements'],
|
|
524
|
+
callableOwner: ['class_declaration', 'object_declaration'],
|
|
525
|
+
callableOwnerBody: ['class_body'],
|
|
526
|
+
reusableDeclaration: ['function_declaration', 'class_declaration', 'object_declaration'],
|
|
527
|
+
reusableContainer: ['import_list'],
|
|
528
|
+
bindingContext: ['import_header'],
|
|
529
|
+
bindingDeclaration: ['property_declaration', 'type_alias'],
|
|
530
|
+
bindingIdentifier: ['simple_identifier', 'type_identifier'],
|
|
402
531
|
block: ['statements', 'block'],
|
|
403
532
|
},
|
|
404
533
|
envReads: (root) => envKeysFrom(root, /System\.getenv|getenv/, ['call_expression']),
|
|
405
534
|
swallowedError: catchBased(['catch_block'], undefined, /^(println|print|log|logger|Log)\b/),
|
|
535
|
+
reusableAcrossFiles(node) {
|
|
536
|
+
return !/\bprivate\b/.test(declarationHeader(node));
|
|
537
|
+
},
|
|
406
538
|
};
|
|
407
539
|
/*
|
|
408
540
|
* Swift is deliberately absent.
|
|
@@ -427,6 +559,14 @@ const SWIFT_DISABLED = {
|
|
|
427
559
|
ifStatement: ['if_statement'],
|
|
428
560
|
bail: ['control_transfer_statement'],
|
|
429
561
|
declaration: ['function_declaration', 'class_declaration', 'protocol_declaration'],
|
|
562
|
+
callable: ['function_declaration'],
|
|
563
|
+
callableBody: ['function_body', 'statements'],
|
|
564
|
+
callableOwner: [
|
|
565
|
+
'class_declaration', 'struct_declaration', 'protocol_declaration',
|
|
566
|
+
'extension_declaration', 'enum_declaration',
|
|
567
|
+
],
|
|
568
|
+
callableOwnerBody: ['class_body', 'protocol_body', 'enum_class_body'],
|
|
569
|
+
reusableDeclaration: ['function_declaration', 'class_declaration', 'protocol_declaration'],
|
|
430
570
|
block: ['statements', 'function_body'],
|
|
431
571
|
},
|
|
432
572
|
envReads: (root) => envKeysFrom(root, /ProcessInfo|environment/, ['call_expression', 'subscript_expression']),
|
|
@@ -438,11 +578,17 @@ const RUBY = {
|
|
|
438
578
|
grammar: 'tree-sitter-ruby',
|
|
439
579
|
nodes: {
|
|
440
580
|
...COMMON_NODES,
|
|
441
|
-
ifStatement: ['if', 'if_modifier'],
|
|
442
|
-
ifCondition: 'condition',
|
|
443
|
-
ifBody: 'consequence',
|
|
581
|
+
ifStatement: ['if', 'if_modifier', 'unless', 'unless_modifier'],
|
|
444
582
|
bail: ['return', 'break', 'next'],
|
|
445
583
|
declaration: ['method', 'singleton_method', 'class', 'module'],
|
|
584
|
+
callable: ['method', 'singleton_method'],
|
|
585
|
+
callableBody: ['body_statement'],
|
|
586
|
+
callableOwner: ['class', 'module'],
|
|
587
|
+
callableOwnerBody: ['body_statement'],
|
|
588
|
+
reusableDeclaration: ['method', 'singleton_method', 'class', 'module'],
|
|
589
|
+
bindingContext: ['call'],
|
|
590
|
+
bindingDeclaration: ['assignment'],
|
|
591
|
+
bindingIdentifier: ['identifier', 'constant'],
|
|
446
592
|
block: ['body_statement', 'do_block', 'block'],
|
|
447
593
|
},
|
|
448
594
|
envReads: (root) => envKeysFrom(root, /ENV/, ['element_reference', 'call']),
|
|
@@ -477,6 +623,12 @@ const RUBY = {
|
|
|
477
623
|
}
|
|
478
624
|
return out;
|
|
479
625
|
},
|
|
626
|
+
fileConstraints(root) {
|
|
627
|
+
return root.namedChildren
|
|
628
|
+
.filter((node) => COMMON_NODES.comment.includes(node.type) && node.startPosition.row <= 1)
|
|
629
|
+
.map((node) => node.text.trim())
|
|
630
|
+
.filter((text) => /^#\s*(?:frozen_string_literal|encoding|coding)\s*[:=]/.test(text));
|
|
631
|
+
},
|
|
480
632
|
};
|
|
481
633
|
void SWIFT_DISABLED;
|
|
482
634
|
const SOLIDITY = {
|
|
@@ -489,6 +641,16 @@ const SOLIDITY = {
|
|
|
489
641
|
// revert and require are how a contract refuses, alongside plain return
|
|
490
642
|
bail: ['return_statement', 'revert_statement', 'break_statement', 'continue_statement'],
|
|
491
643
|
declaration: ['function_definition', 'contract_declaration', 'modifier_definition'],
|
|
644
|
+
callable: ['function_definition', 'modifier_definition'],
|
|
645
|
+
callableBody: ['function_body', 'block_statement'],
|
|
646
|
+
callableOwner: ['contract_declaration'],
|
|
647
|
+
callableOwnerBody: ['contract_body'],
|
|
648
|
+
reusableDeclaration: ['function_definition', 'contract_declaration'],
|
|
649
|
+
bindingContext: ['import_directive', 'pragma_directive'],
|
|
650
|
+
bindingDeclaration: [
|
|
651
|
+
'constant_variable_declaration', 'struct_declaration', 'enum_declaration',
|
|
652
|
+
'user_defined_value_type_definition',
|
|
653
|
+
],
|
|
492
654
|
block: ['block_statement', 'function_body', 'contract_body'],
|
|
493
655
|
},
|
|
494
656
|
swallowedError: catchBased(['catch_clause'], 'body', /^(emit|console\.log)/),
|