@appland/scanner 1.67.0 → 1.68.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/CHANGELOG.md +13 -0
- package/built/cli/scan/breakpoint.js +65 -0
- package/built/cli/scan/command.js +28 -18
- package/built/cli/scan/interactiveScan.js +34 -0
- package/built/cli/scan/singleScan.js +6 -14
- package/built/cli/scan/ui/interactiveProgess.js +147 -0
- package/built/cli/scan/ui/scanContext.js +67 -0
- package/built/cli/scan/ui/state/addBreakpoint.js +120 -0
- package/built/cli/scan/ui/state/eval.js +41 -0
- package/built/cli/scan/ui/state/hint.js +23 -0
- package/built/cli/scan/ui/state/hitBreakpoint.js +68 -0
- package/built/cli/scan/ui/state/initial.js +58 -0
- package/built/cli/scan/ui/state/scan.js +33 -0
- package/built/cli/scan/ui/state.js +2 -0
- package/built/cli/scan/ui/userInteraction.js +97 -0
- package/built/progressReporter.js +2 -0
- package/built/ruleChecker.js +22 -10
- package/built/rules/lib/parseRuleDescription.js +4 -1
- package/built/rules/lib/util.js +31 -1
- package/doc/rules/http-500.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# [@appland/scanner-v1.68.0](https://github.com/applandinc/appmap-js/compare/@appland/scanner-v1.67.0...@appland/scanner-v1.68.0) (2022-08-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Allow rule doc to be missing ([85a22fc](https://github.com/applandinc/appmap-js/commit/85a22fcaf777ab3794300d3ad52057be6ace4a87))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Enhanced breakpoints ([b338bc9](https://github.com/applandinc/appmap-js/commit/b338bc9c1beebf3fbc78fb57bb72e4738c0ed5e6))
|
|
12
|
+
* Interactive rule evaluator ([d0a0846](https://github.com/applandinc/appmap-js/commit/d0a08466ed0e92484a4c195c74cbb737a2cb40d6))
|
|
13
|
+
|
|
1
14
|
# [@appland/scanner-v1.67.0](https://github.com/applandinc/appmap-js/compare/@appland/scanner-v1.66.0...@appland/scanner-v1.67.0) (2022-08-10)
|
|
2
15
|
|
|
3
16
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BreakOnCodeObject = exports.BreakOnLabel = exports.BreakOnEvent = exports.BreakOnCounter = void 0;
|
|
4
|
+
class BreakOnCounter {
|
|
5
|
+
constructor(counter) {
|
|
6
|
+
this.counter = counter;
|
|
7
|
+
}
|
|
8
|
+
condition(context) {
|
|
9
|
+
return this.counter === context.counter;
|
|
10
|
+
}
|
|
11
|
+
toString() {
|
|
12
|
+
return `(${this.counter})`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.BreakOnCounter = BreakOnCounter;
|
|
16
|
+
class BreakOnEvent {
|
|
17
|
+
constructor(eventName) {
|
|
18
|
+
this.eventName = makeRegularExpression(eventName);
|
|
19
|
+
}
|
|
20
|
+
condition(context) {
|
|
21
|
+
return this.eventName.test(context.eventName);
|
|
22
|
+
}
|
|
23
|
+
toString() {
|
|
24
|
+
return this.eventName.toString();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.BreakOnEvent = BreakOnEvent;
|
|
28
|
+
class BreakOnLabel {
|
|
29
|
+
constructor(label) {
|
|
30
|
+
this.label = makeRegularExpression(label);
|
|
31
|
+
}
|
|
32
|
+
condition(context) {
|
|
33
|
+
if (!context.event)
|
|
34
|
+
return false;
|
|
35
|
+
return !![...context.event.codeObject.labels].find((label) => this.label.test(label));
|
|
36
|
+
}
|
|
37
|
+
toString() {
|
|
38
|
+
return this.label.toString();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.BreakOnLabel = BreakOnLabel;
|
|
42
|
+
class BreakOnCodeObject {
|
|
43
|
+
constructor(codeObject) {
|
|
44
|
+
this.codeObject = makeRegularExpression(codeObject);
|
|
45
|
+
}
|
|
46
|
+
condition(context) {
|
|
47
|
+
if (!context.event)
|
|
48
|
+
return false;
|
|
49
|
+
return this.codeObject.test(context.event.codeObject.fqid);
|
|
50
|
+
}
|
|
51
|
+
toString() {
|
|
52
|
+
return this.codeObject.toString();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.BreakOnCodeObject = BreakOnCodeObject;
|
|
56
|
+
function escapeRegexp(expr) {
|
|
57
|
+
return new RegExp(expr.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'));
|
|
58
|
+
}
|
|
59
|
+
function makeRegularExpression(expr) {
|
|
60
|
+
if (expr.length < 2)
|
|
61
|
+
return escapeRegexp(expr);
|
|
62
|
+
if (expr.startsWith('/') && expr.endsWith('/'))
|
|
63
|
+
return new RegExp(expr);
|
|
64
|
+
return escapeRegexp(expr);
|
|
65
|
+
}
|
|
@@ -22,11 +22,16 @@ const singleScan_1 = __importDefault(require("./singleScan"));
|
|
|
22
22
|
const watchScan_1 = __importDefault(require("./watchScan"));
|
|
23
23
|
const configurationProvider_1 = require("../../configuration/configurationProvider");
|
|
24
24
|
const handleWorkingDirectory_1 = require("../handleWorkingDirectory");
|
|
25
|
+
const interactiveScan_1 = __importDefault(require("./interactiveScan"));
|
|
25
26
|
exports.default = {
|
|
26
27
|
command: 'scan',
|
|
27
28
|
describe: 'Scan AppMaps for code behavior findings',
|
|
28
29
|
builder(args) {
|
|
29
30
|
(0, scanArgs_1.default)(args);
|
|
31
|
+
args.option('interactive', {
|
|
32
|
+
describe: 'scan in interactive mode',
|
|
33
|
+
alias: 'i',
|
|
34
|
+
});
|
|
30
35
|
args.option('appmap-file', {
|
|
31
36
|
describe: 'single file to scan, or repeat this option to scan multiple specific files',
|
|
32
37
|
alias: 'f',
|
|
@@ -50,7 +55,7 @@ exports.default = {
|
|
|
50
55
|
handler(options) {
|
|
51
56
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
57
|
let { appmapDir } = options;
|
|
53
|
-
const { appmapFile,
|
|
58
|
+
const { appmapFile, directory, interactive, config: configFile, verbose: isVerbose, all: reportAllFindings, watch, app: appIdArg, apiKey, ide, reportFile, } = options;
|
|
54
59
|
if (isVerbose) {
|
|
55
60
|
(0, util_1.verbose)(true);
|
|
56
61
|
}
|
|
@@ -64,33 +69,38 @@ exports.default = {
|
|
|
64
69
|
if (reportAllFindings && watch) {
|
|
65
70
|
throw new errors_1.ValidationError(`Don't use --all with --watch, because in watch mode all findings are reported`);
|
|
66
71
|
}
|
|
67
|
-
if (appIdArg && watch) {
|
|
68
|
-
throw new errors_1.ValidationError(`Don't use --app with --watch, because in watch mode all findings are reported`);
|
|
69
|
-
}
|
|
70
72
|
if (!appmapFile && !appmapDir) {
|
|
71
73
|
appmapDir = (yield (0, appmapDirFromConfig_1.appmapDirFromConfig)()) || '.';
|
|
72
74
|
}
|
|
73
75
|
if (appmapDir)
|
|
74
76
|
yield (0, validateFile_1.default)('directory', appmapDir);
|
|
77
|
+
let appId = appIdArg;
|
|
78
|
+
if (!reportAllFindings)
|
|
79
|
+
appId = yield (0, resolveAppId_1.default)(appIdArg, appmapDir);
|
|
75
80
|
if (watch) {
|
|
76
81
|
const watchAppMapDir = appmapDir;
|
|
77
|
-
return (0, watchScan_1.default)({ appmapDir: watchAppMapDir, configFile
|
|
82
|
+
return (0, watchScan_1.default)({ appId, appmapDir: watchAppMapDir, configFile });
|
|
78
83
|
}
|
|
79
84
|
else {
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
85
|
+
const configuration = yield (0, configurationProvider_1.parseConfigFile)(configFile);
|
|
86
|
+
if (interactive) {
|
|
87
|
+
return (0, interactiveScan_1.default)({
|
|
88
|
+
appmapFile,
|
|
89
|
+
appmapDir,
|
|
90
|
+
configuration,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
return (0, singleScan_1.default)({
|
|
95
|
+
appmapFile,
|
|
96
|
+
appmapDir,
|
|
97
|
+
configuration,
|
|
98
|
+
reportAllFindings,
|
|
99
|
+
appId,
|
|
100
|
+
ide,
|
|
101
|
+
reportFile,
|
|
102
|
+
});
|
|
83
103
|
}
|
|
84
|
-
const configData = yield (0, configurationProvider_1.parseConfigFile)(config);
|
|
85
|
-
return (0, singleScan_1.default)({
|
|
86
|
-
appmapFile,
|
|
87
|
-
appmapDir,
|
|
88
|
-
configData,
|
|
89
|
-
reportAllFindings,
|
|
90
|
-
appId,
|
|
91
|
-
ide,
|
|
92
|
-
reportFile,
|
|
93
|
-
});
|
|
94
104
|
}
|
|
95
105
|
});
|
|
96
106
|
},
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const configurationProvider_1 = require("../../configuration/configurationProvider");
|
|
16
|
+
const util_1 = require("../../rules/lib/util");
|
|
17
|
+
const validateFile_1 = __importDefault(require("../validateFile"));
|
|
18
|
+
const scanContext_1 = __importDefault(require("./ui/scanContext"));
|
|
19
|
+
const initial_1 = __importDefault(require("./ui/state/initial"));
|
|
20
|
+
function interactiveScan(options) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
const { appmapFile, appmapDir, configuration } = options;
|
|
23
|
+
const checks = yield (0, configurationProvider_1.loadConfig)(configuration);
|
|
24
|
+
const files = yield (0, util_1.collectAppMapFiles)(appmapFile, appmapDir);
|
|
25
|
+
yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () { return (0, validateFile_1.default)('file', file); })));
|
|
26
|
+
const context = new scanContext_1.default(checks, files);
|
|
27
|
+
let state = initial_1.default;
|
|
28
|
+
while (state) {
|
|
29
|
+
const newState = yield state(context);
|
|
30
|
+
state = newState;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
exports.default = interactiveScan;
|
|
@@ -12,10 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const util_1 = require("util");
|
|
16
15
|
const promises_1 = require("fs/promises");
|
|
17
|
-
const glob_1 = require("glob");
|
|
18
|
-
const validateFile_1 = __importDefault(require("../validateFile"));
|
|
19
16
|
const scanner_1 = __importDefault(require("./scanner"));
|
|
20
17
|
const errors_1 = require("../../errors");
|
|
21
18
|
const findings_1 = require("../../findings");
|
|
@@ -23,19 +20,14 @@ const findingsReport_1 = __importDefault(require("../../report/findingsReport"))
|
|
|
23
20
|
const summaryReport_1 = __importDefault(require("../../report/summaryReport"));
|
|
24
21
|
const formatReport_1 = require("./formatReport");
|
|
25
22
|
const telemetry_1 = __importDefault(require("../../telemetry"));
|
|
23
|
+
const util_1 = require("../../rules/lib/util");
|
|
24
|
+
const validateFile_1 = __importDefault(require("../validateFile"));
|
|
26
25
|
function singleScan(options) {
|
|
27
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
-
const { appmapFile, appmapDir,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
files = yield glob(`${appmapDir}/**/*.appmap.json`);
|
|
33
|
-
}
|
|
34
|
-
if (appmapFile) {
|
|
35
|
-
files = typeof appmapFile === 'string' ? [appmapFile] : appmapFile;
|
|
36
|
-
yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () { return (0, validateFile_1.default)('file', file); })));
|
|
37
|
-
}
|
|
38
|
-
const scanner = yield (0, scanner_1.default)(reportAllFindings, configData, files).catch((error) => {
|
|
27
|
+
const { appmapFile, appmapDir, configuration, reportAllFindings, appId, ide, reportFile } = options;
|
|
28
|
+
const files = yield (0, util_1.collectAppMapFiles)(appmapFile, appmapDir);
|
|
29
|
+
yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () { return (0, validateFile_1.default)('file', file); })));
|
|
30
|
+
const scanner = yield (0, scanner_1.default)(reportAllFindings, configuration, files).catch((error) => {
|
|
39
31
|
throw new errors_1.ValidationError(error.message + '\nUse --all to perform an offline scan.');
|
|
40
32
|
});
|
|
41
33
|
const startTime = Date.now();
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const events_1 = __importDefault(require("events"));
|
|
16
|
+
class InteractiveProgress extends events_1.default {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.breakpoints = [];
|
|
20
|
+
this.depth = 0;
|
|
21
|
+
this.counter = 0;
|
|
22
|
+
this.initialize();
|
|
23
|
+
}
|
|
24
|
+
initialize() {
|
|
25
|
+
this.depth = 0;
|
|
26
|
+
this.counter = 0;
|
|
27
|
+
}
|
|
28
|
+
addBreakpoint(breakpoint) {
|
|
29
|
+
this.breakpoints.push(breakpoint);
|
|
30
|
+
}
|
|
31
|
+
removeBreakpoint(breakpoint) {
|
|
32
|
+
this.breakpoints = this.breakpoints.filter((b) => b !== breakpoint);
|
|
33
|
+
}
|
|
34
|
+
get prefix() {
|
|
35
|
+
const counterStr = `(${this.counter}) `;
|
|
36
|
+
return (counterStr + ' '.repeat(this.depth).padStart(10 + this.depth * 2 - counterStr.length, ' '));
|
|
37
|
+
}
|
|
38
|
+
resume() {
|
|
39
|
+
if (!this.breakpointResolver)
|
|
40
|
+
return;
|
|
41
|
+
this.breakpointResolver();
|
|
42
|
+
this.breakpointResolver = undefined;
|
|
43
|
+
}
|
|
44
|
+
breakOn(eventName, variables) {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
const context = Object.assign({ eventName, appMap: this.appMap, appMapFileName: this.appMapFileName, check: this.check, scope: this.scope, counter: this.counter, depth: this.depth }, variables);
|
|
47
|
+
const hitBreakpoint = this.breakpoints.find((b) => b.condition(context));
|
|
48
|
+
if (!hitBreakpoint)
|
|
49
|
+
return;
|
|
50
|
+
this.emit('breakpoint', hitBreakpoint, context);
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
this.breakpointResolver = resolve;
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
beginAppMap(appMapFileName, appMap) {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
console.log(`${this.prefix}beginAppMap: ${appMapFileName}`);
|
|
59
|
+
this.depth += 1;
|
|
60
|
+
this.appMapFileName = appMapFileName;
|
|
61
|
+
this.appMap = appMap;
|
|
62
|
+
yield this.breakOn('beginAppMap', {});
|
|
63
|
+
this.counter += 1;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
beginCheck(check) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
console.log(`${this.prefix}beginCheck: ${check}`);
|
|
69
|
+
this.depth += 1;
|
|
70
|
+
this.check = check;
|
|
71
|
+
yield this.breakOn('beginCheck', {});
|
|
72
|
+
this.counter += 1;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
filterScope(scopeName, scope) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
console.log(`${this.prefix}filterScope: ${scopeName} ${scope}`);
|
|
78
|
+
yield this.breakOn('filterScope', {});
|
|
79
|
+
this.counter += 1;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
enterScope(scope) {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
console.log(`${this.prefix}enterScope: ${scope}`);
|
|
85
|
+
this.depth += 1;
|
|
86
|
+
this.scope = scope;
|
|
87
|
+
yield this.breakOn('enterScope', {});
|
|
88
|
+
this.counter += 1;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
filterEvent(event) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
console.log(`${this.prefix}filterEvent: ${event}`);
|
|
94
|
+
this.event = event;
|
|
95
|
+
yield this.breakOn('filterEvent', { event });
|
|
96
|
+
this.event = undefined;
|
|
97
|
+
this.counter += 1;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
matchResult(event, matchResult) {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
console.log(`${this.prefix}matchResult: ${matchResult}`);
|
|
103
|
+
this.event = event;
|
|
104
|
+
yield this.breakOn('matchResult', { matchResult });
|
|
105
|
+
this.event = undefined;
|
|
106
|
+
this.counter += 1;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
matchEvent(event, _appMapIndex) {
|
|
110
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
console.log(`${this.prefix}matchEvent: ${event}`);
|
|
112
|
+
this.event = event;
|
|
113
|
+
yield this.breakOn('matchEvent', {});
|
|
114
|
+
this.event = undefined;
|
|
115
|
+
this.counter += 1;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
leaveScope() {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
this.depth -= 1;
|
|
121
|
+
console.log(`${this.prefix}leaveScope`);
|
|
122
|
+
yield this.breakOn('leaveScope', {});
|
|
123
|
+
this.scope = undefined;
|
|
124
|
+
this.counter += 1;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
endCheck() {
|
|
128
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
this.depth -= 1;
|
|
130
|
+
console.log(`${this.prefix}endCheck`);
|
|
131
|
+
yield this.breakOn('endCheck', {});
|
|
132
|
+
this.check = undefined;
|
|
133
|
+
this.counter += 1;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
endAppMap() {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
this.depth -= 1;
|
|
139
|
+
console.log(`${this.prefix}endAppMap`);
|
|
140
|
+
yield this.breakOn('endAppMap', {});
|
|
141
|
+
this.appMap = undefined;
|
|
142
|
+
this.appMapFileName = undefined;
|
|
143
|
+
this.counter += 1;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.default = InteractiveProgress;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const models_1 = require("@appland/models");
|
|
16
|
+
const console_1 = require("console");
|
|
17
|
+
const events_1 = __importDefault(require("events"));
|
|
18
|
+
const promises_1 = require("fs/promises");
|
|
19
|
+
const appMapIndex_1 = __importDefault(require("../../../appMapIndex"));
|
|
20
|
+
const ruleChecker_1 = __importDefault(require("../../../ruleChecker"));
|
|
21
|
+
const interactiveProgess_1 = __importDefault(require("./interactiveProgess"));
|
|
22
|
+
class ScanContext extends events_1.default {
|
|
23
|
+
constructor(checks, files) {
|
|
24
|
+
super();
|
|
25
|
+
this.checks = checks;
|
|
26
|
+
this.files = files;
|
|
27
|
+
this.isScanning = false;
|
|
28
|
+
this.progress = new interactiveProgess_1.default();
|
|
29
|
+
this.checker = new ruleChecker_1.default(this.progress);
|
|
30
|
+
this.progress.on('breakpoint', (breakpoint) => {
|
|
31
|
+
this.emit('breakpoint', breakpoint);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
scan() {
|
|
35
|
+
if (this.isScanning) {
|
|
36
|
+
this.progress.resume();
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.progress.initialize();
|
|
40
|
+
this.doScan();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
doScan() {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
(0, console_1.assert)(!this.isScanning, 'Scanning already in progress');
|
|
46
|
+
this.isScanning = true;
|
|
47
|
+
const findings = [];
|
|
48
|
+
for (let fileIndex = 0; fileIndex < this.files.length; fileIndex++) {
|
|
49
|
+
const fileName = this.files[fileIndex];
|
|
50
|
+
const appMapData = yield (0, promises_1.readFile)(fileName, 'utf8');
|
|
51
|
+
const appMap = (0, models_1.buildAppMap)(appMapData).normalize().build();
|
|
52
|
+
const appMapIndex = new appMapIndex_1.default(appMap);
|
|
53
|
+
yield this.progress.beginAppMap(fileName, appMap);
|
|
54
|
+
for (let checkIndex = 0; checkIndex < this.checks.length; checkIndex++) {
|
|
55
|
+
const check = this.checks[checkIndex];
|
|
56
|
+
yield this.progress.beginCheck(check);
|
|
57
|
+
yield this.checker.check(fileName, appMapIndex, check, findings);
|
|
58
|
+
yield this.progress.endCheck();
|
|
59
|
+
}
|
|
60
|
+
yield this.progress.endAppMap();
|
|
61
|
+
}
|
|
62
|
+
this.isScanning = false;
|
|
63
|
+
this.emit('complete', findings);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.default = ScanContext;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const readline_1 = require("readline");
|
|
16
|
+
const breakpoint_1 = require("../../breakpoint");
|
|
17
|
+
const userInteraction_1 = __importDefault(require("../userInteraction"));
|
|
18
|
+
const scan_1 = __importDefault(require("./scan"));
|
|
19
|
+
const history = [];
|
|
20
|
+
function addBreakpoint(context) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
const onCounter = () => __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const { sequenceNumber } = yield userInteraction_1.default.prompt({
|
|
24
|
+
name: 'sequenceNumber',
|
|
25
|
+
type: 'number',
|
|
26
|
+
message: 'Sequence number:',
|
|
27
|
+
});
|
|
28
|
+
return new breakpoint_1.BreakOnCounter(Number(sequenceNumber));
|
|
29
|
+
});
|
|
30
|
+
const onEventName = () => __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
const { eventName } = yield userInteraction_1.default.prompt({
|
|
32
|
+
name: 'eventName',
|
|
33
|
+
type: 'input',
|
|
34
|
+
message: 'Event name:',
|
|
35
|
+
});
|
|
36
|
+
return new breakpoint_1.BreakOnEvent(eventName);
|
|
37
|
+
});
|
|
38
|
+
const onLabel = () => __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
const { labelName } = yield userInteraction_1.default.prompt({
|
|
40
|
+
name: 'labelName',
|
|
41
|
+
type: 'input',
|
|
42
|
+
message: 'Event label:',
|
|
43
|
+
});
|
|
44
|
+
return new breakpoint_1.BreakOnLabel(labelName);
|
|
45
|
+
});
|
|
46
|
+
const onCodeObject = () => __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
let codeObjectName;
|
|
48
|
+
if (!context.progress.appMap) {
|
|
49
|
+
codeObjectName = (yield userInteraction_1.default.prompt({
|
|
50
|
+
name: 'codeObjectName',
|
|
51
|
+
type: 'input',
|
|
52
|
+
message: 'Code object:',
|
|
53
|
+
}))['codeObjectName'];
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
let codeObjectIds = [];
|
|
57
|
+
const collectCodeObjectNames = (codeObject) => {
|
|
58
|
+
codeObjectIds.push(codeObject.fqid);
|
|
59
|
+
};
|
|
60
|
+
context.progress.appMap.classMap.visit(collectCodeObjectNames);
|
|
61
|
+
codeObjectIds = codeObjectIds.sort();
|
|
62
|
+
const codeObjectCompleter = (line) => {
|
|
63
|
+
let options = codeObjectIds.filter((id) => id.startsWith(line));
|
|
64
|
+
if (options.length === 0)
|
|
65
|
+
options = codeObjectIds
|
|
66
|
+
.filter((id) => id.includes(line))
|
|
67
|
+
.map((id) => id.slice(id.indexOf(line)));
|
|
68
|
+
return [options, line];
|
|
69
|
+
};
|
|
70
|
+
codeObjectName = yield new Promise((resolve, _reject) => {
|
|
71
|
+
const rl = (0, readline_1.createInterface)({
|
|
72
|
+
input: process.stdin,
|
|
73
|
+
output: process.stdout,
|
|
74
|
+
completer: codeObjectCompleter,
|
|
75
|
+
history,
|
|
76
|
+
historySize: 1000,
|
|
77
|
+
removeHistoryDuplicates: true,
|
|
78
|
+
prompt: 'Code object: ',
|
|
79
|
+
tabSize: 4,
|
|
80
|
+
});
|
|
81
|
+
let response = '';
|
|
82
|
+
rl.on('line', (data) => {
|
|
83
|
+
response = data;
|
|
84
|
+
rl.close();
|
|
85
|
+
});
|
|
86
|
+
rl.on('close', () => {
|
|
87
|
+
resolve(response);
|
|
88
|
+
});
|
|
89
|
+
rl.prompt();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (codeObjectName && codeObjectName !== '')
|
|
93
|
+
return new breakpoint_1.BreakOnCodeObject(codeObjectName);
|
|
94
|
+
});
|
|
95
|
+
const choices = {
|
|
96
|
+
'break at sequence number': onCounter,
|
|
97
|
+
'break on label': onLabel,
|
|
98
|
+
'break on code object': onCodeObject,
|
|
99
|
+
'break on event name': onEventName,
|
|
100
|
+
quit: null,
|
|
101
|
+
};
|
|
102
|
+
userInteraction_1.default.progress(`Choose a breakpoint type, and enter the criteria.`);
|
|
103
|
+
userInteraction_1.default.progress(`NOTE: label, code object, and event name breakpoints can be regular expressions.`);
|
|
104
|
+
userInteraction_1.default.progress(` To enter a regular expression, use the syntax: /expr/`);
|
|
105
|
+
const { action: actionName } = yield userInteraction_1.default.prompt({
|
|
106
|
+
name: 'action',
|
|
107
|
+
type: 'list',
|
|
108
|
+
message: 'How would you like to proceed?:',
|
|
109
|
+
choices: Object.keys(choices),
|
|
110
|
+
});
|
|
111
|
+
const action = choices[actionName];
|
|
112
|
+
if (!action)
|
|
113
|
+
return scan_1.default;
|
|
114
|
+
const breakpoint = yield action();
|
|
115
|
+
if (breakpoint)
|
|
116
|
+
context.progress.addBreakpoint(breakpoint);
|
|
117
|
+
return scan_1.default;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
exports.default = addBreakpoint;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
16
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
17
|
+
const userInteraction_1 = __importDefault(require("../userInteraction"));
|
|
18
|
+
const hitBreakpoint_1 = __importDefault(require("./hitBreakpoint"));
|
|
19
|
+
const util_1 = require("util");
|
|
20
|
+
function initial(context) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
const { expression } = yield userInteraction_1.default.prompt({
|
|
23
|
+
name: 'expression',
|
|
24
|
+
type: 'input',
|
|
25
|
+
message: 'Enter expression:',
|
|
26
|
+
});
|
|
27
|
+
let result;
|
|
28
|
+
try {
|
|
29
|
+
result = ((check, appMap, scope, event) => {
|
|
30
|
+
return eval(expression);
|
|
31
|
+
})(context.progress.check, context.progress.appMap, context.progress.scope, context.progress.event);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
console.log(err);
|
|
35
|
+
}
|
|
36
|
+
if (result)
|
|
37
|
+
console.log((0, util_1.inspect)(result));
|
|
38
|
+
return hitBreakpoint_1.default;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
exports.default = initial;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const userInteraction_1 = __importDefault(require("../userInteraction"));
|
|
16
|
+
const hitBreakpoint_1 = __importDefault(require("./hitBreakpoint"));
|
|
17
|
+
function hint(_context) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
userInteraction_1.default.progress([`Some suggested expressions:`, 'appMap', 'check', 'event', 'scope'].join('\n'));
|
|
20
|
+
return hitBreakpoint_1.default;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
exports.default = hint;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
const userInteraction_1 = __importDefault(require("../userInteraction"));
|
|
39
|
+
const initial_1 = __importDefault(require("./initial"));
|
|
40
|
+
function hitBreakpoint(context) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const choices = {
|
|
44
|
+
'show hints': 'hint',
|
|
45
|
+
'evaluate expression': 'eval',
|
|
46
|
+
'add breakpoint': 'addBreakpoint',
|
|
47
|
+
continue: 'scan',
|
|
48
|
+
quit: null,
|
|
49
|
+
};
|
|
50
|
+
userInteraction_1.default.progress(`In breakpoint: ${context.breakpoint}`);
|
|
51
|
+
if (context.progress.appMapFileName) {
|
|
52
|
+
const line = context.progress.appMapFileName;
|
|
53
|
+
const eventId = ((_a = context.progress.event) === null || _a === void 0 ? void 0 : _a.id) || ((_b = context.progress.scope) === null || _b === void 0 ? void 0 : _b.id);
|
|
54
|
+
userInteraction_1.default.progress([line, eventId].filter(Boolean).join(':'));
|
|
55
|
+
}
|
|
56
|
+
const { action: actionName } = yield userInteraction_1.default.prompt({
|
|
57
|
+
name: 'action',
|
|
58
|
+
type: 'list',
|
|
59
|
+
message: 'Choose action:',
|
|
60
|
+
choices: Object.keys(choices),
|
|
61
|
+
});
|
|
62
|
+
const action = choices[actionName];
|
|
63
|
+
if (!action)
|
|
64
|
+
return initial_1.default;
|
|
65
|
+
return (yield Promise.resolve().then(() => __importStar(require(`./${action}`)))).default;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
exports.default = hitBreakpoint;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
const userInteraction_1 = __importDefault(require("../userInteraction"));
|
|
39
|
+
function initial(_context) {
|
|
40
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
const choices = {
|
|
42
|
+
'add breakpoint': 'addBreakpoint',
|
|
43
|
+
'run scan': 'scan',
|
|
44
|
+
quit: null,
|
|
45
|
+
};
|
|
46
|
+
const { action: actionName } = yield userInteraction_1.default.prompt({
|
|
47
|
+
name: 'action',
|
|
48
|
+
type: 'list',
|
|
49
|
+
message: 'How would you like to proceed?:',
|
|
50
|
+
choices: Object.keys(choices),
|
|
51
|
+
});
|
|
52
|
+
const action = choices[actionName];
|
|
53
|
+
if (!action)
|
|
54
|
+
return;
|
|
55
|
+
return (yield Promise.resolve().then(() => __importStar(require(`./${action}`)))).default;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
exports.default = initial;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const hitBreakpoint_1 = __importDefault(require("./hitBreakpoint"));
|
|
16
|
+
const initial_1 = __importDefault(require("./initial"));
|
|
17
|
+
function scan(context) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
context.on('breakpoint', (breakpoint) => {
|
|
21
|
+
context.removeAllListeners();
|
|
22
|
+
context.breakpoint = breakpoint;
|
|
23
|
+
resolve(hitBreakpoint_1.default);
|
|
24
|
+
});
|
|
25
|
+
context.on('complete', () => {
|
|
26
|
+
context.removeAllListeners();
|
|
27
|
+
resolve(initial_1.default);
|
|
28
|
+
});
|
|
29
|
+
context.scan();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
exports.default = scan;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.UserInteraction = void 0;
|
|
16
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
17
|
+
const ora_1 = __importDefault(require("ora"));
|
|
18
|
+
const boxen_1 = __importDefault(require("boxen"));
|
|
19
|
+
const util_1 = require("../../../rules/lib/util");
|
|
20
|
+
// KEG: Sorry, copied from packages/cli/src/cmds/userInteraction.ts
|
|
21
|
+
class UserInteraction {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.spinner = (0, ora_1.default)();
|
|
24
|
+
}
|
|
25
|
+
prompt(questions, opts) {
|
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
const wasSpinning = this.spinner.isSpinning;
|
|
28
|
+
if (wasSpinning) {
|
|
29
|
+
this.spinner.stop();
|
|
30
|
+
this.spinner.clear();
|
|
31
|
+
}
|
|
32
|
+
const result = yield inquirer_1.default.prompt(questions);
|
|
33
|
+
if (wasSpinning && !(opts === null || opts === void 0 ? void 0 : opts.supressSpinner)) {
|
|
34
|
+
this.spinner.start();
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
continue(msg) {
|
|
40
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
yield inquirer_1.default.prompt({ type: 'input', name: 'confirm', message: msg });
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
confirm(msg) {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
const { confirm } = yield inquirer_1.default.prompt({
|
|
47
|
+
type: 'confirm',
|
|
48
|
+
name: 'confirm',
|
|
49
|
+
message: msg,
|
|
50
|
+
});
|
|
51
|
+
return confirm;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
progress(msg) {
|
|
55
|
+
console.log(msg);
|
|
56
|
+
}
|
|
57
|
+
success(msg, align = 'center') {
|
|
58
|
+
if (this.spinner.isSpinning) {
|
|
59
|
+
this.spinner.succeed();
|
|
60
|
+
}
|
|
61
|
+
if (msg) {
|
|
62
|
+
console.log((0, boxen_1.default)(msg, {
|
|
63
|
+
padding: 1,
|
|
64
|
+
margin: 1,
|
|
65
|
+
borderStyle: 'round',
|
|
66
|
+
textAlignment: align,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
error(msg) {
|
|
71
|
+
if (this.spinner.isSpinning) {
|
|
72
|
+
this.spinner.fail();
|
|
73
|
+
}
|
|
74
|
+
if (msg) {
|
|
75
|
+
console.error('');
|
|
76
|
+
console.error(msg);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
warn(msg) {
|
|
80
|
+
console.error(msg);
|
|
81
|
+
}
|
|
82
|
+
get status() {
|
|
83
|
+
return this.spinner.text;
|
|
84
|
+
}
|
|
85
|
+
set status(value) {
|
|
86
|
+
if (this.spinner.isSpinning) {
|
|
87
|
+
this.spinner.succeed();
|
|
88
|
+
}
|
|
89
|
+
this.spinner.text = value;
|
|
90
|
+
if (!this.spinner.isSpinning && !(0, util_1.verbose)()) {
|
|
91
|
+
this.spinner.start();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.UserInteraction = UserInteraction;
|
|
96
|
+
const UI = new UserInteraction();
|
|
97
|
+
exports.default = UI;
|
package/built/ruleChecker.js
CHANGED
|
@@ -24,7 +24,8 @@ const eventUtil_1 = require("./eventUtil");
|
|
|
24
24
|
const hashV1_1 = __importDefault(require("./algorithms/hash/hashV1"));
|
|
25
25
|
const hashV2_1 = __importDefault(require("./algorithms/hash/hashV2"));
|
|
26
26
|
class RuleChecker {
|
|
27
|
-
constructor() {
|
|
27
|
+
constructor(progress) {
|
|
28
|
+
this.progress = progress;
|
|
28
29
|
this.scopes = {
|
|
29
30
|
root: new rootScope_1.default(),
|
|
30
31
|
command: new commandScope_1.default(),
|
|
@@ -33,15 +34,14 @@ class RuleChecker {
|
|
|
33
34
|
transaction: new sqlTransactionScope_1.default(),
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
|
-
check(
|
|
37
|
+
check(appMapFileName, appMapIndex, check, findings) {
|
|
37
38
|
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
-
const scope = check.scope;
|
|
39
39
|
if ((0, util_1.verbose)()) {
|
|
40
|
-
console.warn(`Checking AppMap ${appMapIndex.appMap.name} with scope ${scope}`);
|
|
40
|
+
console.warn(`Checking AppMap ${appMapIndex.appMap.name} with scope ${check.scope}`);
|
|
41
41
|
}
|
|
42
|
-
const scopeIterator = this.scopes[scope];
|
|
42
|
+
const scopeIterator = this.scopes[check.scope];
|
|
43
43
|
if (!scopeIterator) {
|
|
44
|
-
throw new errors_1.AbortError(`Invalid scope name "${scope}"`);
|
|
44
|
+
throw new errors_1.AbortError(`Invalid scope name "${check.scope}"`);
|
|
45
45
|
}
|
|
46
46
|
const callEvents = function* () {
|
|
47
47
|
const events = appMapIndex.appMap.events;
|
|
@@ -53,22 +53,28 @@ class RuleChecker {
|
|
|
53
53
|
if ((0, util_1.verbose)()) {
|
|
54
54
|
console.warn(`Scope ${scope.scope}`);
|
|
55
55
|
}
|
|
56
|
+
if (this.progress)
|
|
57
|
+
yield this.progress.filterScope(check.scope, scope.scope);
|
|
56
58
|
const checkInstance = new checkInstance_1.default(check);
|
|
57
59
|
if (!check.filterScope(scope.scope, appMapIndex)) {
|
|
58
60
|
continue;
|
|
59
61
|
}
|
|
62
|
+
if (this.progress)
|
|
63
|
+
yield this.progress.enterScope(scope.scope);
|
|
60
64
|
if (checkInstance.enumerateScope) {
|
|
61
65
|
for (const event of scope.events()) {
|
|
62
|
-
yield this.checkEvent(event, scope.scope,
|
|
66
|
+
yield this.checkEvent(event, scope.scope, appMapFileName, appMapIndex, checkInstance, findings);
|
|
63
67
|
}
|
|
64
68
|
}
|
|
65
69
|
else {
|
|
66
|
-
yield this.checkEvent(scope.scope, scope.scope,
|
|
70
|
+
yield this.checkEvent(scope.scope, scope.scope, appMapFileName, appMapIndex, checkInstance, findings);
|
|
67
71
|
}
|
|
72
|
+
if (this.progress)
|
|
73
|
+
yield this.progress.leaveScope();
|
|
68
74
|
}
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
|
-
checkEvent(event, scope,
|
|
77
|
+
checkEvent(event, scope, appMapFileName, appMapIndex, checkInstance, findings) {
|
|
72
78
|
return __awaiter(this, void 0, void 0, function* () {
|
|
73
79
|
if (!event.isCall()) {
|
|
74
80
|
return;
|
|
@@ -82,6 +88,8 @@ class RuleChecker {
|
|
|
82
88
|
}
|
|
83
89
|
return;
|
|
84
90
|
}
|
|
91
|
+
if (this.progress)
|
|
92
|
+
yield this.progress.filterEvent(event);
|
|
85
93
|
if (!checkInstance.filterEvent(event, appMapIndex)) {
|
|
86
94
|
return;
|
|
87
95
|
}
|
|
@@ -117,7 +125,7 @@ class RuleChecker {
|
|
|
117
125
|
relatedEvents.push((0, eventUtil_1.cloneEvent)(event));
|
|
118
126
|
});
|
|
119
127
|
return {
|
|
120
|
-
appMapFile,
|
|
128
|
+
appMapFile: appMapFileName,
|
|
121
129
|
checkId: checkInstance.checkId,
|
|
122
130
|
ruleId: checkInstance.ruleId,
|
|
123
131
|
ruleTitle: checkInstance.title,
|
|
@@ -134,7 +142,11 @@ class RuleChecker {
|
|
|
134
142
|
participatingEvents: Object.fromEntries(Object.entries(participatingEvents).map(([k, v]) => [k, (0, eventUtil_1.cloneEvent)(v)])),
|
|
135
143
|
};
|
|
136
144
|
};
|
|
145
|
+
if (this.progress)
|
|
146
|
+
yield this.progress.matchEvent(event, appMapIndex);
|
|
137
147
|
const matchResult = yield checkInstance.ruleLogic.matcher(event, appMapIndex, checkInstance.filterEvent.bind(checkInstance));
|
|
148
|
+
if (this.progress)
|
|
149
|
+
yield this.progress.matchResult(event, matchResult);
|
|
138
150
|
const numFindings = findings.length;
|
|
139
151
|
if (matchResult === true) {
|
|
140
152
|
let finding;
|
|
@@ -7,7 +7,10 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
7
7
|
const path_1 = require("path");
|
|
8
8
|
const util_1 = require("./util");
|
|
9
9
|
function parseRuleDescription(id) {
|
|
10
|
-
const
|
|
10
|
+
const docPath = (0, path_1.join)(__dirname, `../../../doc/rules/${(0, util_1.dasherize)(id)}.md`);
|
|
11
|
+
if (!fs_1.default.existsSync(docPath))
|
|
12
|
+
return `No doc exists for rule ${id}`;
|
|
13
|
+
const content = fs_1.default.readFileSync(docPath, 'utf-8');
|
|
11
14
|
const propertiesContent = content.match(/---\n((?:.*\n)+)---\n((?:.*\n)+?)##?#?/);
|
|
12
15
|
if (!propertiesContent) {
|
|
13
16
|
// This is probably a new doc that doesn't have front matter yet.
|
package/built/rules/lib/util.js
CHANGED
|
@@ -1,7 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
2
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.verbose = exports.toRegExpArray = exports.responseContentType = exports.toRegExp = exports.providesAuthentication = exports.pluralize = exports.dasherize = exports.camelize = exports.parseValue = exports.isRoot = exports.ideLink = exports.isTruthy = exports.isFalsey = exports.emptyValue = exports.capitalize = exports.appMapDir = void 0;
|
|
15
|
+
exports.verbose = exports.toRegExpArray = exports.responseContentType = exports.toRegExp = exports.providesAuthentication = exports.pluralize = exports.dasherize = exports.camelize = exports.parseValue = exports.isRoot = exports.ideLink = exports.isTruthy = exports.isFalsey = exports.emptyValue = exports.capitalize = exports.appMapDir = exports.collectAppMapFiles = void 0;
|
|
4
16
|
const path_1 = require("path");
|
|
17
|
+
const util_1 = require("util");
|
|
18
|
+
const glob_1 = require("glob");
|
|
19
|
+
const assert_1 = __importDefault(require("assert"));
|
|
20
|
+
function collectAppMapFiles(appmapFile, appmapDir) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
let files = [];
|
|
23
|
+
if (appmapDir) {
|
|
24
|
+
const glob = (0, util_1.promisify)(glob_1.glob);
|
|
25
|
+
files = yield glob(`${appmapDir}/**/*.appmap.json`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
(0, assert_1.default)(appmapFile, 'Either appmapDir or appmapFile is required');
|
|
29
|
+
files = typeof appmapFile === 'string' ? [appmapFile] : appmapFile;
|
|
30
|
+
}
|
|
31
|
+
return files;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
exports.collectAppMapFiles = collectAppMapFiles;
|
|
5
35
|
let isVerbose = false;
|
|
6
36
|
function verbose(v = null) {
|
|
7
37
|
if (v === true || v === false) {
|
package/doc/rules/http-500.md
CHANGED
|
@@ -8,7 +8,7 @@ impactDomain: Stability
|
|
|
8
8
|
scope: http_server_request
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
Identifies when an HTTP server
|
|
11
|
+
Identifies when an HTTP server request has returned a 500 status code. HTTP 500 status code
|
|
12
12
|
generally indicate an unanticipated problem in the backend that is not handled in a predictable way.
|
|
13
13
|
500 status codes are also hard for client code to handle, because they don't indicate any particular
|
|
14
14
|
problem or suggest a solution.
|