@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.
Files changed (54) hide show
  1. package/README.md +65 -8
  2. package/bin/safe-chain.js +1 -81
  3. package/package.json +1 -1
  4. package/src/api/aikido.js +93 -18
  5. package/src/config/cliArguments.js +24 -1
  6. package/src/config/configFile.js +64 -6
  7. package/src/config/environmentVariables.js +13 -2
  8. package/src/config/settings.js +53 -4
  9. package/src/main.js +6 -2
  10. package/src/packagemanager/_shared/commandErrors.js +17 -0
  11. package/src/packagemanager/bun/createBunPackageManager.js +2 -7
  12. package/src/packagemanager/npm/runNpmCommand.js +2 -7
  13. package/src/packagemanager/npx/runNpxCommand.js +2 -7
  14. package/src/packagemanager/pip/runPipCommand.js +2 -7
  15. package/src/packagemanager/pipx/runPipXCommand.js +2 -7
  16. package/src/packagemanager/pnpm/runPnpmCommand.js +3 -7
  17. package/src/packagemanager/poetry/createPoetryPackageManager.js +2 -7
  18. package/src/packagemanager/uv/runUvCommand.js +2 -7
  19. package/src/packagemanager/yarn/runYarnCommand.js +2 -7
  20. package/src/registryProxy/certBundle.js +25 -3
  21. package/src/registryProxy/http-utils.js +63 -0
  22. package/src/registryProxy/interceptors/createInterceptorForEcoSystem.js +1 -1
  23. package/src/registryProxy/interceptors/interceptorBuilder.js +37 -4
  24. package/src/registryProxy/interceptors/minimumPackageAgeExclusions.js +33 -0
  25. package/src/registryProxy/interceptors/npm/modifyNpmInfo.js +18 -41
  26. package/src/registryProxy/interceptors/npm/npmInterceptor.js +47 -2
  27. package/src/registryProxy/interceptors/npm/parseNpmPackageUrl.js +20 -3
  28. package/src/registryProxy/interceptors/pip/modifyPipInfo.js +167 -0
  29. package/src/registryProxy/interceptors/pip/modifyPipJsonResponse.js +176 -0
  30. package/src/registryProxy/interceptors/pip/parsePipPackageUrl.js +162 -0
  31. package/src/registryProxy/interceptors/pip/pipInterceptor.js +122 -0
  32. package/src/registryProxy/interceptors/pip/pipMetadataResponseUtils.js +27 -0
  33. package/src/registryProxy/interceptors/pip/pipMetadataVersionUtils.js +131 -0
  34. package/src/registryProxy/interceptors/suppressedVersionsState.js +21 -0
  35. package/src/registryProxy/mitmRequestHandler.js +12 -6
  36. package/src/registryProxy/registryProxy.js +72 -9
  37. package/src/scanning/newPackagesDatabaseBuilder.js +71 -0
  38. package/src/scanning/newPackagesDatabaseWarnings.js +17 -0
  39. package/src/scanning/newPackagesListCache.js +126 -0
  40. package/src/scanning/packageNameVariants.js +29 -0
  41. package/src/shell-integration/setup.js +7 -3
  42. package/src/shell-integration/shellDetection.js +2 -0
  43. package/src/shell-integration/supported-shells/bash.js +19 -1
  44. package/src/shell-integration/supported-shells/fish.js +18 -0
  45. package/src/shell-integration/supported-shells/powershell.js +18 -0
  46. package/src/shell-integration/supported-shells/windowsPowershell.js +18 -0
  47. package/src/shell-integration/supported-shells/zsh.js +19 -1
  48. package/src/shell-integration/teardown.js +7 -1
  49. package/src/ultimate/ultimateTroubleshooting.js +1 -1
  50. package/src/installation/downloadAgent.js +0 -125
  51. package/src/installation/installOnMacOS.js +0 -155
  52. package/src/installation/installOnWindows.js +0 -203
  53. package/src/installation/installUltimate.js +0 -35
  54. 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
- }