@aikidosec/safe-chain 1.4.3 → 1.4.7
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 +65 -8
- package/bin/safe-chain.js +1 -81
- package/package.json +1 -1
- package/src/api/aikido.js +93 -18
- package/src/config/cliArguments.js +24 -1
- package/src/config/configFile.js +64 -6
- package/src/config/environmentVariables.js +13 -2
- package/src/config/settings.js +53 -4
- package/src/main.js +6 -2
- package/src/packagemanager/_shared/commandErrors.js +17 -0
- package/src/packagemanager/bun/createBunPackageManager.js +2 -7
- package/src/packagemanager/npm/runNpmCommand.js +2 -7
- package/src/packagemanager/npx/runNpxCommand.js +2 -7
- package/src/packagemanager/pip/runPipCommand.js +2 -7
- package/src/packagemanager/pipx/runPipXCommand.js +2 -7
- package/src/packagemanager/pnpm/runPnpmCommand.js +3 -7
- package/src/packagemanager/poetry/createPoetryPackageManager.js +2 -7
- package/src/packagemanager/uv/runUvCommand.js +2 -7
- package/src/packagemanager/yarn/runYarnCommand.js +2 -7
- package/src/registryProxy/certBundle.js +25 -3
- package/src/registryProxy/http-utils.js +63 -0
- package/src/registryProxy/interceptors/createInterceptorForEcoSystem.js +1 -1
- package/src/registryProxy/interceptors/interceptorBuilder.js +37 -4
- package/src/registryProxy/interceptors/minimumPackageAgeExclusions.js +33 -0
- package/src/registryProxy/interceptors/npm/modifyNpmInfo.js +18 -41
- package/src/registryProxy/interceptors/npm/npmInterceptor.js +47 -2
- package/src/registryProxy/interceptors/npm/parseNpmPackageUrl.js +20 -3
- package/src/registryProxy/interceptors/pip/modifyPipInfo.js +167 -0
- package/src/registryProxy/interceptors/pip/modifyPipJsonResponse.js +176 -0
- package/src/registryProxy/interceptors/pip/parsePipPackageUrl.js +162 -0
- package/src/registryProxy/interceptors/pip/pipInterceptor.js +122 -0
- package/src/registryProxy/interceptors/pip/pipMetadataResponseUtils.js +27 -0
- package/src/registryProxy/interceptors/pip/pipMetadataVersionUtils.js +131 -0
- package/src/registryProxy/interceptors/suppressedVersionsState.js +21 -0
- package/src/registryProxy/mitmRequestHandler.js +12 -6
- package/src/registryProxy/registryProxy.js +72 -9
- package/src/scanning/newPackagesDatabaseBuilder.js +71 -0
- package/src/scanning/newPackagesDatabaseWarnings.js +17 -0
- package/src/scanning/newPackagesListCache.js +126 -0
- package/src/scanning/packageNameVariants.js +29 -0
- package/src/shell-integration/setup.js +7 -3
- package/src/shell-integration/shellDetection.js +2 -0
- package/src/shell-integration/supported-shells/bash.js +19 -1
- package/src/shell-integration/supported-shells/fish.js +18 -0
- package/src/shell-integration/supported-shells/powershell.js +18 -0
- package/src/shell-integration/supported-shells/windowsPowershell.js +18 -0
- package/src/shell-integration/supported-shells/zsh.js +19 -1
- package/src/shell-integration/teardown.js +7 -1
- package/src/ultimate/ultimateTroubleshooting.js +1 -1
- package/src/installation/downloadAgent.js +0 -125
- package/src/installation/installOnMacOS.js +0 -155
- package/src/installation/installOnWindows.js +0 -203
- package/src/installation/installUltimate.js +0 -35
- package/src/registryProxy/interceptors/pipInterceptor.js +0 -132
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { getPipCustomRegistries } from "../../config/settings.js";
|
|
2
|
-
import { isMalwarePackage } from "../../scanning/audit/index.js";
|
|
3
|
-
import { interceptRequests } from "./interceptorBuilder.js";
|
|
4
|
-
|
|
5
|
-
const knownPipRegistries = [
|
|
6
|
-
"files.pythonhosted.org",
|
|
7
|
-
"pypi.org",
|
|
8
|
-
"pypi.python.org",
|
|
9
|
-
"pythonhosted.org",
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @param {string} url
|
|
14
|
-
* @returns {import("./interceptorBuilder.js").Interceptor | undefined}
|
|
15
|
-
*/
|
|
16
|
-
export function pipInterceptorForUrl(url) {
|
|
17
|
-
const customRegistries = getPipCustomRegistries();
|
|
18
|
-
const registries = [...knownPipRegistries, ...customRegistries];
|
|
19
|
-
const registry = registries.find((reg) => url.includes(reg));
|
|
20
|
-
|
|
21
|
-
if (registry) {
|
|
22
|
-
return buildPipInterceptor(registry);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @param {string} registry
|
|
30
|
-
* @returns {import("./interceptorBuilder.js").Interceptor | undefined}
|
|
31
|
-
*/
|
|
32
|
-
function buildPipInterceptor(registry) {
|
|
33
|
-
return interceptRequests(async (reqContext) => {
|
|
34
|
-
const { packageName, version } = parsePipPackageFromUrl(
|
|
35
|
-
reqContext.targetUrl,
|
|
36
|
-
registry
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
// Normalize underscores to hyphens for DB matching, as PyPI allows underscores in distribution names.
|
|
40
|
-
// Per python, packages that differ only by hyphen vs underscore are considered the same.
|
|
41
|
-
const hyphenName = packageName?.includes("_") ? packageName.replace(/_/g, "-") : packageName;
|
|
42
|
-
|
|
43
|
-
const isMalicious =
|
|
44
|
-
await isMalwarePackage(packageName, version)
|
|
45
|
-
|| await isMalwarePackage(hyphenName, version);
|
|
46
|
-
|
|
47
|
-
if (isMalicious) {
|
|
48
|
-
reqContext.blockMalware(packageName, version);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @param {string} url
|
|
55
|
-
* @param {string} registry
|
|
56
|
-
* @returns {{packageName: string | undefined, version: string | undefined}}
|
|
57
|
-
*/
|
|
58
|
-
function parsePipPackageFromUrl(url, registry) {
|
|
59
|
-
let packageName, version;
|
|
60
|
-
|
|
61
|
-
// Basic validation
|
|
62
|
-
if (!registry || typeof url !== "string") {
|
|
63
|
-
return { packageName, version };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Quick sanity check on the URL + parse
|
|
67
|
-
let urlObj;
|
|
68
|
-
try {
|
|
69
|
-
urlObj = new URL(url);
|
|
70
|
-
} catch {
|
|
71
|
-
return { packageName, version };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Get the last path segment (filename) and decode it (strip query & fragment automatically)
|
|
75
|
-
const lastSegment = urlObj.pathname.split("/").filter(Boolean).pop();
|
|
76
|
-
if (!lastSegment) {
|
|
77
|
-
return { packageName, version };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const filename = decodeURIComponent(lastSegment);
|
|
81
|
-
|
|
82
|
-
// Parse Python package downloads from PyPI/pythonhosted.org
|
|
83
|
-
// Example wheel: https://files.pythonhosted.org/packages/xx/yy/requests-2.28.1-py3-none-any.whl
|
|
84
|
-
// Example sdist: https://files.pythonhosted.org/packages/xx/yy/requests-2.28.1.tar.gz
|
|
85
|
-
|
|
86
|
-
// Wheel (.whl) and Poetry's preflight metadata (.whl.metadata)
|
|
87
|
-
// Examples:
|
|
88
|
-
// foo_bar-2.0.0-py3-none-any.whl
|
|
89
|
-
// foo_bar-2.0.0-py3-none-any.whl.metadata
|
|
90
|
-
const wheelExtRe = /\.whl(?:\.metadata)?$/;
|
|
91
|
-
const wheelExtMatch = filename.match(wheelExtRe);
|
|
92
|
-
if (wheelExtMatch) {
|
|
93
|
-
const base = filename.replace(wheelExtRe, "");
|
|
94
|
-
const firstDash = base.indexOf("-");
|
|
95
|
-
if (firstDash > 0) {
|
|
96
|
-
const dist = base.slice(0, firstDash); // may contain underscores
|
|
97
|
-
const rest = base.slice(firstDash + 1); // version + the rest of tags
|
|
98
|
-
const secondDash = rest.indexOf("-");
|
|
99
|
-
const rawVersion = secondDash >= 0 ? rest.slice(0, secondDash) : rest;
|
|
100
|
-
packageName = dist;
|
|
101
|
-
version = rawVersion;
|
|
102
|
-
// Reject "latest" as it's a placeholder, not a real version
|
|
103
|
-
// When version is "latest", this signals the URL doesn't contain actual version info
|
|
104
|
-
// Returning undefined allows the request (see registryProxy.js isAllowedUrl)
|
|
105
|
-
if (version === "latest" || !packageName || !version) {
|
|
106
|
-
return { packageName: undefined, version: undefined };
|
|
107
|
-
}
|
|
108
|
-
return { packageName, version };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Source dist (sdist) and potential metadata sidecars (e.g., .tar.gz.metadata)
|
|
113
|
-
const sdistExtWithMetadataRe = /\.(tar\.gz|zip|tar\.bz2|tar\.xz)(\.metadata)?$/i;
|
|
114
|
-
const sdistExtMatch = filename.match(sdistExtWithMetadataRe);
|
|
115
|
-
if (sdistExtMatch) {
|
|
116
|
-
const base = filename.replace(sdistExtWithMetadataRe, "");
|
|
117
|
-
const lastDash = base.lastIndexOf("-");
|
|
118
|
-
if (lastDash > 0 && lastDash < base.length - 1) {
|
|
119
|
-
packageName = base.slice(0, lastDash);
|
|
120
|
-
version = base.slice(lastDash + 1);
|
|
121
|
-
// Reject "latest" as it's a placeholder, not a real version
|
|
122
|
-
// When version is "latest", this signals the URL doesn't contain actual version info
|
|
123
|
-
// Returning undefined allows the request (see registryProxy.js isAllowedUrl)
|
|
124
|
-
if (version === "latest" || !packageName || !version) {
|
|
125
|
-
return { packageName: undefined, version: undefined };
|
|
126
|
-
}
|
|
127
|
-
return { packageName, version };
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// Unknown file type or invalid
|
|
131
|
-
return { packageName: undefined, version: undefined };
|
|
132
|
-
}
|