tewip-ai 0.18.5 → 0.18.6
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.
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** The path a web finding says is being served, e.g. "/.env" from "GET http://host/.env answered". */
|
|
2
|
+
function servedPath(item) {
|
|
3
|
+
if (item.kind !== 'web')
|
|
4
|
+
return null;
|
|
5
|
+
// Take the whole address and ask the URL parser for the path. Pulling the path out with a regular
|
|
6
|
+
// expression captured "//127.0.0.1:3211/.env", host and all, and then matched nothing.
|
|
7
|
+
const found = /GET\s+(\S+)\s+answered/.exec(item.why);
|
|
8
|
+
if (!found)
|
|
9
|
+
return null;
|
|
10
|
+
try {
|
|
11
|
+
return new URL(found[1]).pathname;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return found[1].startsWith('/') ? found[1] : null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** The file a secret finding is in, from "a database URL with a password in it in .env:1". */
|
|
18
|
+
const fileOf = (item) => (item.kind === 'secret' ? (item.where.split(':')[0] ?? '') : '');
|
|
19
|
+
/**
|
|
20
|
+
* Where one check changes what another means. Today that is one case, and it is the one that
|
|
21
|
+
* matters most: a secret sitting in a file the application serves.
|
|
22
|
+
*/
|
|
23
|
+
export function reconcile(items) {
|
|
24
|
+
const served = new Set(items
|
|
25
|
+
.map(servedPath)
|
|
26
|
+
.filter((p) => Boolean(p))
|
|
27
|
+
.map((p) => p.replace(/^\//, '').toLowerCase()));
|
|
28
|
+
if (served.size === 0)
|
|
29
|
+
return items;
|
|
30
|
+
return items.map((item) => {
|
|
31
|
+
const file = fileOf(item).replace(/\\/g, '/').toLowerCase();
|
|
32
|
+
if (!file || !served.has(file) || item.verdict === 'accepted')
|
|
33
|
+
return item;
|
|
34
|
+
return {
|
|
35
|
+
...item,
|
|
36
|
+
verdict: 'real-risk',
|
|
37
|
+
severity: 'critical',
|
|
38
|
+
why: `${item.why} - but the running application serves /${file} to anyone who asks, so this credential is public`,
|
|
39
|
+
next: `treat this credential as leaked and rotate it now, then stop serving /${file}`,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -9,6 +9,7 @@ import { auditDependencies } from './deps.js';
|
|
|
9
9
|
import { scanSecrets, sourceFiles } from './secrets.js';
|
|
10
10
|
import { readAccepted, triageDependency, triageSecret, triageWeb, worstFirst } from './triage.js';
|
|
11
11
|
import { isLocal, scanWeb } from './web.js';
|
|
12
|
+
import { reconcile } from './reconcile.js';
|
|
12
13
|
import { loadVerdicts, saidByAPerson, verdictFor } from '../learn/verdicts.js';
|
|
13
14
|
import { readZapReport, zapAvailable, zapInstructions } from './zap.js';
|
|
14
15
|
const CODE = /\.(?:[cm]?[jt]sx?|py|rb|go|java|kt|cs|php|rs|vue|svelte)$/;
|
|
@@ -98,6 +99,11 @@ export async function runSecure(cmd) {
|
|
|
98
99
|
else if (cmd.url) {
|
|
99
100
|
notChecked.push(`a full web scan. Tewip's own check is passive; for the deep one: ${zapInstructions(cmd.url, zapAvailable(root))}`);
|
|
100
101
|
}
|
|
102
|
+
// One check can change what another means: a secret in a file the application serves is not
|
|
103
|
+
// "never left the machine", whatever git says about it.
|
|
104
|
+
const joined = reconcile(items);
|
|
105
|
+
items.length = 0;
|
|
106
|
+
items.push(...joined);
|
|
101
107
|
// A person's decision about a finding wins over Tewip's, and says whose it was.
|
|
102
108
|
const corrections = loadVerdicts(root);
|
|
103
109
|
const decided = items.map((item) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tewip-ai",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.6",
|
|
4
4
|
"description": "Tewip finds why each failing test failed - real bug, flaky test, environment, test data or changed selector - for any stack, from the command line, CI, or your editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"test-failures",
|