@appland/scanner 1.60.0 → 1.62.1

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 CHANGED
@@ -1,3 +1,25 @@
1
+ # [@appland/scanner-v1.62.1](https://github.com/applandinc/appmap-js/compare/@appland/scanner-v1.62.0...@appland/scanner-v1.62.1) (2022-07-13)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * More reliable detection of appmap changes by scanner watch ([b0cc14d](https://github.com/applandinc/appmap-js/commit/b0cc14d61b7e27248975c35022a8cd4da070337b))
7
+
8
+ # [@appland/scanner-v1.62.0](https://github.com/applandinc/appmap-js/compare/@appland/scanner-v1.61.0...@appland/scanner-v1.62.0) (2022-07-12)
9
+
10
+
11
+ ### Features
12
+
13
+ * Support working directory -d arg ([2bd280f](https://github.com/applandinc/appmap-js/commit/2bd280fa2e6bb2d13c6bc1c578b1b78c3a62492a))
14
+ * upload command uses appmap_dir from config ([801b75b](https://github.com/applandinc/appmap-js/commit/801b75bf008b6188d4aacd143a1992149aafebd3))
15
+
16
+ # [@appland/scanner-v1.61.0](https://github.com/applandinc/appmap-js/compare/@appland/scanner-v1.60.0...@appland/scanner-v1.61.0) (2022-07-11)
17
+
18
+
19
+ ### Features
20
+
21
+ * Add participating events to each finding ([f3e8033](https://github.com/applandinc/appmap-js/commit/f3e80332833ec3305ef530d89b12763781a8c85b))
22
+
1
23
  # [@appland/scanner-v1.60.0](https://github.com/applandinc/appmap-js/compare/@appland/scanner-v1.59.2...@appland/scanner-v1.60.0) (2022-06-30)
2
24
 
3
25
 
@@ -14,7 +14,7 @@ function default_1(secrets, e) {
14
14
  if ((0, util_1.verbose)()) {
15
15
  console.warn(`Secret generated: ${secret}`);
16
16
  }
17
- secrets.add(secret);
17
+ secrets.push({ generatorEvent: e, value: secret });
18
18
  }
19
19
  }
20
20
  exports.default = default_1;
@@ -0,0 +1,29 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.appmapDirFromConfig = void 0;
13
+ const fs_1 = require("fs");
14
+ const promises_1 = require("fs/promises");
15
+ const js_yaml_1 = require("js-yaml");
16
+ const util_1 = require("util");
17
+ function appmapDirFromConfig() {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ const appMapConfigExists = yield (0, util_1.promisify)(fs_1.exists)('appmap.yml');
20
+ if (appMapConfigExists) {
21
+ const appMapConfigData = (0, js_yaml_1.load)((yield (0, promises_1.readFile)('appmap.yml')).toString());
22
+ if (appMapConfigData && typeof appMapConfigData === 'object') {
23
+ const configData = appMapConfigData;
24
+ return configData['appmap_dir'];
25
+ }
26
+ }
27
+ });
28
+ }
29
+ exports.appmapDirFromConfig = appmapDirFromConfig;
@@ -18,6 +18,7 @@ const util_1 = require("util");
18
18
  const configurationProvider_1 = require("../../configuration/configurationProvider");
19
19
  const errors_1 = require("../../errors");
20
20
  const util_2 = require("../../rules/lib/util");
21
+ const appmapDirFromConfig_1 = require("../appmapDirFromConfig");
21
22
  const findings_1 = require("../../findings");
22
23
  const findingsReport_1 = __importDefault(require("../../report/findingsReport"));
23
24
  const summaryReport_1 = __importDefault(require("../../report/summaryReport"));
@@ -30,6 +31,7 @@ const updateCommitStatus_1 = __importDefault(require("../updateCommitStatus"));
30
31
  const reportUploadURL_1 = __importDefault(require("../reportUploadURL"));
31
32
  const fail_1 = __importDefault(require("../fail"));
32
33
  const codeVersionArgs_1 = __importDefault(require("../codeVersionArgs"));
34
+ const handleWorkingDirectory_1 = require("../handleWorkingDirectory");
33
35
  exports.default = {
34
36
  command: 'ci',
35
37
  describe: 'Scan AppMaps, report findings to AppMap Server, and update SCM status',
@@ -59,17 +61,16 @@ exports.default = {
59
61
  handler(options) {
60
62
  return __awaiter(this, void 0, void 0, function* () {
61
63
  let { appmapDir } = options;
62
- const { config, verbose: isVerbose, fail: failOption, app: appIdArg, reportFile, upload: doUpload, updateCommitStatus: updateCommitStatusOption, mergeKey, commit, branch, environment, } = options;
64
+ const { config, verbose: isVerbose, fail: failOption, app: appIdArg, directory, reportFile, upload: doUpload, updateCommitStatus: updateCommitStatusOption, mergeKey, commit, branch, environment, } = options;
63
65
  if (isVerbose) {
64
66
  (0, util_2.verbose)(true);
65
67
  }
68
+ (0, handleWorkingDirectory_1.handleWorkingDirectory)(directory);
66
69
  if (!appmapDir) {
67
- appmapDir = yield (0, util_2.appmapDirFromConfig)();
68
- }
69
- if (!appmapDir) {
70
- appmapDir = yield (0, util_2.appmapDirFromConfig)();
71
- throw new errors_1.ValidationError('--appmap-dir is required');
70
+ appmapDir = yield (0, appmapDirFromConfig_1.appmapDirFromConfig)();
72
71
  }
72
+ if (!appmapDir)
73
+ throw new errors_1.ValidationError('appmapDir must be provided as a command option, or available in appmap.yml');
73
74
  yield (0, validateFile_1.default)('directory', appmapDir);
74
75
  const appId = yield (0, resolveAppId_1.default)(appIdArg, appmapDir);
75
76
  const glob = (0, util_1.promisify)(glob_1.glob);
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleWorkingDirectory = void 0;
4
+ function handleWorkingDirectory(directory) {
5
+ if (directory)
6
+ process.chdir(directory);
7
+ }
8
+ exports.handleWorkingDirectory = handleWorkingDirectory;
@@ -14,12 +14,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const errors_1 = require("../../errors");
16
16
  const util_1 = require("../../rules/lib/util");
17
+ const appmapDirFromConfig_1 = require("../appmapDirFromConfig");
17
18
  const validateFile_1 = __importDefault(require("../validateFile"));
18
19
  const scanArgs_1 = __importDefault(require("../scanArgs"));
19
20
  const resolveAppId_1 = __importDefault(require("../resolveAppId"));
20
21
  const singleScan_1 = __importDefault(require("./singleScan"));
21
22
  const watchScan_1 = __importDefault(require("./watchScan"));
22
23
  const configurationProvider_1 = require("../../configuration/configurationProvider");
24
+ const handleWorkingDirectory_1 = require("../handleWorkingDirectory");
23
25
  exports.default = {
24
26
  command: 'scan',
25
27
  describe: 'Scan AppMaps for code behavior findings',
@@ -48,10 +50,11 @@ exports.default = {
48
50
  handler(options) {
49
51
  return __awaiter(this, void 0, void 0, function* () {
50
52
  let { appmapDir } = options;
51
- const { appmapFile, config, verbose: isVerbose, all: reportAllFindings, watch, app: appIdArg, apiKey, ide, reportFile, } = options;
53
+ const { appmapFile, config, directory, verbose: isVerbose, all: reportAllFindings, watch, app: appIdArg, apiKey, ide, reportFile, } = options;
52
54
  if (isVerbose) {
53
55
  (0, util_1.verbose)(true);
54
56
  }
57
+ (0, handleWorkingDirectory_1.handleWorkingDirectory)(directory);
55
58
  if (apiKey) {
56
59
  process.env.APPLAND_API_KEY = apiKey;
57
60
  }
@@ -65,7 +68,7 @@ exports.default = {
65
68
  throw new errors_1.ValidationError(`Don't use --app with --watch, because in watch mode all findings are reported`);
66
69
  }
67
70
  if (!appmapFile && !appmapDir) {
68
- appmapDir = (yield (0, util_1.appmapDirFromConfig)()) || '.';
71
+ appmapDir = (yield (0, appmapDirFromConfig_1.appmapDirFromConfig)()) || '.';
69
72
  }
70
73
  if (appmapDir)
71
74
  yield (0, validateFile_1.default)('directory', appmapDir);
@@ -1,9 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  function default_1(args) {
4
+ args.option('directory', {
5
+ describe: 'program working directory',
6
+ type: 'string',
7
+ alias: 'd',
8
+ });
4
9
  args.option('appmap-dir', {
5
10
  describe: 'directory to recursively inspect for AppMaps',
6
- alias: 'd',
7
11
  });
8
12
  args.option('config', {
9
13
  describe: 'path to assertions config file (TypeScript or YAML, check docs for configuration format)',
@@ -14,19 +14,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const promises_1 = require("fs/promises");
16
16
  const util_1 = require("../../rules/lib/util");
17
+ const appmapDirFromConfig_1 = require("../appmapDirFromConfig");
17
18
  const validateFile_1 = __importDefault(require("../validateFile"));
18
19
  const resolveAppId_1 = __importDefault(require("../resolveAppId"));
19
20
  const reportUploadURL_1 = __importDefault(require("../reportUploadURL"));
20
21
  const upload_1 = __importDefault(require("../upload"));
21
22
  const codeVersionArgs_1 = __importDefault(require("../codeVersionArgs"));
23
+ const errors_1 = require("../../errors");
24
+ const handleWorkingDirectory_1 = require("../handleWorkingDirectory");
22
25
  exports.default = {
23
26
  command: 'upload',
24
27
  describe: 'Upload Findings to the AppMap Server',
25
28
  builder(args) {
26
29
  (0, codeVersionArgs_1.default)(args);
30
+ args.option('directory', {
31
+ describe: 'program working directory',
32
+ type: 'string',
33
+ alias: 'd',
34
+ });
27
35
  args.option('appmap-dir', {
28
36
  describe: 'base directory of AppMaps',
29
- alias: 'd',
30
37
  });
31
38
  args.option('report-file', {
32
39
  describe: 'file containing the findings report',
@@ -42,10 +49,17 @@ exports.default = {
42
49
  },
43
50
  handler(options) {
44
51
  return __awaiter(this, void 0, void 0, function* () {
45
- const { verbose: isVerbose, reportFile, appmapDir, app: appIdArg, mergeKey, branch, commit, environment, } = options;
52
+ let { appmapDir } = options;
53
+ const { verbose: isVerbose, directory, reportFile, app: appIdArg, mergeKey, branch, commit, environment, } = options;
46
54
  if (isVerbose) {
47
55
  (0, util_1.verbose)(true);
48
56
  }
57
+ (0, handleWorkingDirectory_1.handleWorkingDirectory)(directory);
58
+ if (!appmapDir) {
59
+ appmapDir = yield (0, appmapDirFromConfig_1.appmapDirFromConfig)();
60
+ }
61
+ if (!appmapDir)
62
+ throw new errors_1.ValidationError('appmapDir must be provided as a command option, or available in appmap.yml');
49
63
  yield (0, validateFile_1.default)('directory', appmapDir);
50
64
  const appId = yield (0, resolveAppId_1.default)(appIdArg, appmapDir);
51
65
  const scanResults = JSON.parse((yield (0, promises_1.readFile)(reportFile)).toString());
@@ -94,8 +94,9 @@ class RuleChecker {
94
94
  if (!checkInstance.filterEvent(event, appMapIndex)) {
95
95
  return;
96
96
  }
97
- const buildFinding = (matchEvent, message, groupMessage, occurranceCount,
98
- // matchEvent will be added to additionalEvents to create the relatedEvents array
97
+ const buildFinding = (matchEvent, participatingEvents, message, groupMessage, occurranceCount,
98
+ // matchEvent will be added to additionalEvents and participatingEvents.values
99
+ // to create the relatedEvents array
99
100
  additionalEvents) => {
100
101
  const findingEvent = matchEvent || event;
101
102
  // Fixes:
@@ -118,12 +119,19 @@ class RuleChecker {
118
119
  return;
119
120
  }
120
121
  uniqueEvents.add(event.id);
121
- relatedEvents.push(event);
122
+ relatedEvents.push((0, eventUtil_1.cloneEvent)(event));
122
123
  });
123
124
  // Update event hash with unique hashes of related events
124
125
  new Set(relatedEvents.map((e) => e.hash)).forEach((eventHash) => {
125
126
  hash.update(eventHash);
126
127
  });
128
+ Object.values(participatingEvents).forEach((event) => {
129
+ if (uniqueEvents.has(event.id)) {
130
+ return;
131
+ }
132
+ uniqueEvents.add(event.id);
133
+ relatedEvents.push((0, eventUtil_1.cloneEvent)(event));
134
+ });
127
135
  return {
128
136
  appMapFile,
129
137
  checkId: checkInstance.checkId,
@@ -138,6 +146,7 @@ class RuleChecker {
138
146
  occurranceCount,
139
147
  relatedEvents: relatedEvents.sort((event) => event.id),
140
148
  impactDomain: checkInstance.checkImpactDomain,
149
+ participatingEvents: Object.fromEntries(Object.entries(participatingEvents).map(([k, v]) => [k, (0, eventUtil_1.cloneEvent)(v)])),
141
150
  };
142
151
  };
143
152
  const matchResult = yield checkInstance.ruleLogic.matcher(event, appMapIndex, checkInstance.filterEvent.bind(checkInstance));
@@ -146,21 +155,21 @@ class RuleChecker {
146
155
  let finding;
147
156
  if (checkInstance.ruleLogic.message) {
148
157
  const message = checkInstance.ruleLogic.message(scope, event);
149
- finding = buildFinding(event, message);
158
+ finding = buildFinding(event, {}, message);
150
159
  }
151
160
  else {
152
- finding = buildFinding(event);
161
+ finding = buildFinding(event, {});
153
162
  }
154
163
  findings.push(finding);
155
164
  }
156
165
  else if (typeof matchResult === 'string') {
157
- const finding = buildFinding(event, matchResult);
166
+ const finding = buildFinding(event, {}, matchResult);
158
167
  finding.message = matchResult;
159
168
  findings.push(finding);
160
169
  }
161
170
  else if (matchResult) {
162
171
  matchResult.forEach((mr) => {
163
- const finding = buildFinding(mr.event, mr.message, mr.groupMessage, mr.occurranceCount, mr.relatedEvents);
172
+ const finding = buildFinding(mr.event, mr.participatingEvents || {}, mr.message, mr.groupMessage, mr.occurranceCount, mr.relatedEvents);
164
173
  findings.push(finding);
165
174
  });
166
175
  }
@@ -173,13 +173,14 @@ function build(options) {
173
173
  .map((cycle) => searchForCycle(cycle, ignoredPackages))
174
174
  .filter((path) => path)
175
175
  .map((path) => {
176
+ path = path;
176
177
  return {
177
178
  event: path[0],
178
179
  message: [
179
180
  'Cycle in package dependency graph',
180
181
  path.map((event) => event.codeObject.packageOf).join(' -> '),
181
182
  ].join(': '),
182
- relatedEvents: path,
183
+ participatingEvents: Object.fromEntries(path.map((event, index) => [`path[${index}]`, event])),
183
184
  };
184
185
  });
185
186
  }
@@ -19,18 +19,21 @@ function build(options) {
19
19
  return !!e.parent && !!e.parent.codeObject.packageOf && calleePattern(e.codeObject.packageOf);
20
20
  }
21
21
  function matcher(e) {
22
+ const parent = e.parent;
23
+ if (!parent)
24
+ return;
22
25
  const packageNamesStr = options.callerPackages
23
26
  .map((config) => config.equal || config.include || config.match)
24
27
  .map(String)
25
28
  .join(' or ');
26
- const parentPackage = e.parent.codeObject.packageOf;
29
+ const parentPackage = parent.codeObject.packageOf;
27
30
  if (!(e.codeObject.packageOf === parentPackage ||
28
31
  callerPatterns.some((pattern) => pattern(parentPackage)))) {
29
32
  return [
30
33
  {
31
34
  event: e,
32
35
  message: `Code object ${e.codeObject.id} was invoked from ${parentPackage}, not from ${packageNamesStr}`,
33
- relatedEvents: [e.parent],
36
+ participatingEvents: { parent },
34
37
  },
35
38
  ];
36
39
  }
@@ -8,7 +8,8 @@ const recordSecrets_1 = __importDefault(require("../analyzer/recordSecrets"));
8
8
  const secretsRegexes_1 = require("../analyzer/secretsRegexes");
9
9
  const parseRuleDescription_1 = __importDefault(require("./lib/parseRuleDescription"));
10
10
  const BCRYPT_REGEXP = /^[$]2[abxy]?[$](?:0[4-9]|[12][0-9]|3[01])[$][./0-9a-zA-Z]{53}$/;
11
- const secrets = new Set();
11
+ const secrets = [];
12
+ const secretStrings = new Set();
12
13
  function stringEquals(e) {
13
14
  if (!e.parameters || !e.receiver || e.parameters.length !== 1) {
14
15
  return false;
@@ -18,7 +19,7 @@ function stringEquals(e) {
18
19
  return BCRYPT_REGEXP.test(str);
19
20
  }
20
21
  function isSecret(str) {
21
- return secrets.has(str) || (0, secretsRegexes_1.looksSecret)(str);
22
+ return secretStrings.has(str) || (0, secretsRegexes_1.looksSecret)(str);
22
23
  }
23
24
  // BCrypted strings are safe to compare using equals()
24
25
  return args.some(isSecret) && !args.some(isBcrypt);
@@ -26,7 +27,12 @@ function stringEquals(e) {
26
27
  function build() {
27
28
  function matcher(e) {
28
29
  if (e.codeObject.labels.has(Secret)) {
30
+ const numSecrets = secrets.length;
29
31
  (0, recordSecrets_1.default)(secrets, e);
32
+ for (let index = numSecrets; index < secrets.length; index++) {
33
+ const secret = secrets[index];
34
+ secretStrings.add(secret.value);
35
+ }
30
36
  }
31
37
  if (e.codeObject.labels.has(StringEquals)) {
32
38
  return stringEquals(e);
@@ -18,14 +18,11 @@ function build() {
18
18
  const missing = creationEvents.length - cancellationEvents.length;
19
19
  if (missing === 0)
20
20
  return;
21
- const result = {
22
- event: event,
23
- message: `${missing} jobs created but not cancelled in this rolled back transaction`,
24
- // if there's a mismatch and there are cancellations we can't tell
25
- // for sure which creations they match, so return everything
26
- relatedEvents: [...creationEvents, ...cancellationEvents],
27
- };
28
- return [result];
21
+ return creationEvents.map((jobCreationEvent) => ({
22
+ event: jobCreationEvent,
23
+ message: `Job created by ${jobCreationEvent.codeObject.prettyName} was not cancelled when the enclosing transaction rolled back`,
24
+ participatingEvents: { beginTransaction: event },
25
+ }));
29
26
  }
30
27
  return {
31
28
  matcher,
@@ -1,33 +1,7 @@
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
- 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.appmapDirFromConfig = void 0;
13
- const fs_1 = require("fs");
14
- const promises_1 = require("fs/promises");
15
- const js_yaml_1 = require("js-yaml");
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;
16
4
  const path_1 = require("path");
17
- const util_1 = require("util");
18
- function appmapDirFromConfig() {
19
- return __awaiter(this, void 0, void 0, function* () {
20
- const appMapConfigExists = yield (0, util_1.promisify)(fs_1.exists)('appmap.yml');
21
- if (appMapConfigExists) {
22
- const appMapConfigData = (0, js_yaml_1.load)((yield (0, promises_1.readFile)('appmap.yml')).toString());
23
- if (appMapConfigData && typeof appMapConfigData === 'object') {
24
- const configData = appMapConfigData;
25
- return configData['appmap_dir'];
26
- }
27
- }
28
- });
29
- }
30
- exports.appmapDirFromConfig = appmapDirFromConfig;
31
5
  let isVerbose = false;
32
6
  function verbose(v = null) {
33
7
  if (v === true || v === false) {
@@ -43,6 +43,7 @@ function build(options) {
43
43
  groupMessage: sql,
44
44
  occurranceCount: occurranceCount,
45
45
  relatedEvents: events.map((e) => e.event),
46
+ participatingEvents: { commonAncestor: ancestor },
46
47
  };
47
48
  };
48
49
  if (occurranceCount >= options.errorLimit) {
@@ -18,12 +18,15 @@ function build(options) {
18
18
  const allowedPackages = (0, matchPattern_1.buildFilters)(options.allowedPackages);
19
19
  const allowedQueries = (0, matchPattern_1.buildFilters)(options.allowedQueries);
20
20
  function matcher(e) {
21
- if (!allowedPackages.some((filter) => filter(e.parent.codeObject.packageOf))) {
21
+ if (!e.parent)
22
+ return;
23
+ const parent = e.parent;
24
+ if (!allowedPackages.some((filter) => filter(parent.codeObject.packageOf))) {
22
25
  return [
23
26
  {
24
27
  event: e,
25
- message: `${e.codeObject.id} is invoked from illegal package ${e.parent.codeObject.packageOf}`,
26
- relatedEvents: [e.parent],
28
+ message: `${e.codeObject.id} is invoked from illegal package ${parent.codeObject.packageOf}`,
29
+ participatingEvents: { parent: parent },
27
30
  },
28
31
  ];
29
32
  }
@@ -32,12 +32,19 @@ const recordSecrets_1 = __importDefault(require("../analyzer/recordSecrets"));
32
32
  const url_1 = require("url");
33
33
  const parseRuleDescription_1 = __importDefault(require("./lib/parseRuleDescription"));
34
34
  class Match {
35
- constructor(pattern, value) {
35
+ constructor(pattern, value, generatorEvent) {
36
36
  this.pattern = pattern;
37
37
  this.value = value;
38
+ this.generatorEvent = generatorEvent;
39
+ }
40
+ static fromPattern(pattern, value) {
41
+ return new Match(pattern, value);
42
+ }
43
+ static fromSecret(secret, value) {
44
+ return new Match(secret.value, value, secret.generatorEvent);
38
45
  }
39
46
  }
40
- const secrets = new Set();
47
+ const secrets = [];
41
48
  const findInLog = (event) => {
42
49
  if (!event.parameters)
43
50
  return;
@@ -45,24 +52,32 @@ const findInLog = (event) => {
45
52
  for (const { value } of event.parameters) {
46
53
  if ((0, util_1.emptyValue)(value))
47
54
  continue;
48
- const patterns = [];
49
55
  if ((0, secretsRegexes_1.looksSecret)(value)) {
50
56
  // Only look for the exact matching regexes if it matches the catchall regex
51
- patterns.push(...Object.values(secretsRegexes_1.default)
57
+ matches.push(...Object.values(secretsRegexes_1.default)
52
58
  .flat()
53
- .filter((re) => re.test(value)));
59
+ .filter((re) => re.test(value))
60
+ .map((re) => Match.fromPattern(re, value)));
54
61
  }
55
62
  for (const secret of secrets) {
56
- if (value.includes(secret))
57
- patterns.push(secret);
63
+ if (value.includes(secret.value)) {
64
+ matches.push(Match.fromSecret(secret, value));
65
+ }
58
66
  }
59
- matches.push(...patterns.map((pattern) => new Match(pattern, value)));
60
67
  }
61
68
  if (matches.length > 0) {
62
- return matches.map((match) => ({
63
- event,
64
- message: `Log event contains secret data: ${match.value}`,
65
- }));
69
+ return matches.map((match) => {
70
+ const { pattern, value } = match;
71
+ const participatingEvents = { logEvent: event };
72
+ if (match.generatorEvent) {
73
+ participatingEvents.generatorEvent = match.generatorEvent;
74
+ }
75
+ return {
76
+ event,
77
+ message: `Log message contains secret ${match.generatorEvent ? match.generatorEvent.codeObject.prettyName || 'data' : 'data'} "${pattern}": ${value}`,
78
+ participatingEvents,
79
+ };
80
+ });
66
81
  }
67
82
  };
68
83
  function build() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appland/scanner",
3
- "version": "1.60.0",
3
+ "version": "1.62.1",
4
4
  "description": "",
5
5
  "bin": "built/cli.js",
6
6
  "files": [
@@ -65,11 +65,11 @@
65
65
  "applicationinsights": "^2.1.4",
66
66
  "async": "^3.2.3",
67
67
  "chalk": "^4.1.2",
68
- "chokidar": "^3.5.3",
68
+ "chokidar": "applandinc/chokidar#fix/new-file-new-directory-race-on-linux",
69
69
  "cli-progress": "^3.11.0",
70
70
  "conf": "^10.0.2",
71
71
  "form-data": "^4.0.0",
72
- "glob": "^7.2.0",
72
+ "glob": "7.2.3",
73
73
  "js-yaml": "^4.1.0",
74
74
  "lru-cache": "^6.0.0",
75
75
  "minimatch": "^3.0.4",