@aikidosec/safe-chain 0.0.1-custom-install-dir
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/LICENSE +674 -0
- package/README.md +537 -0
- package/bin/aikido-bun.js +14 -0
- package/bin/aikido-bunx.js +14 -0
- package/bin/aikido-npm.js +14 -0
- package/bin/aikido-npx.js +14 -0
- package/bin/aikido-pip.js +17 -0
- package/bin/aikido-pip3.js +17 -0
- package/bin/aikido-pipx.js +16 -0
- package/bin/aikido-pnpm.js +14 -0
- package/bin/aikido-pnpx.js +14 -0
- package/bin/aikido-poetry.js +13 -0
- package/bin/aikido-python.js +19 -0
- package/bin/aikido-python3.js +19 -0
- package/bin/aikido-uv.js +16 -0
- package/bin/aikido-uvx.js +16 -0
- package/bin/aikido-yarn.js +14 -0
- package/bin/safe-chain.js +147 -0
- package/docs/Release.md +25 -0
- package/docs/banner.svg +151 -0
- package/docs/safe-package-manager-demo.gif +0 -0
- package/docs/safe-package-manager-demo.png +0 -0
- package/docs/shell-integration.md +149 -0
- package/docs/troubleshooting.md +321 -0
- package/npm-shrinkwrap.json +3180 -0
- package/package.json +71 -0
- package/src/api/aikido.js +187 -0
- package/src/api/npmApi.js +71 -0
- package/src/config/cliArguments.js +161 -0
- package/src/config/configFile.js +327 -0
- package/src/config/environmentVariables.js +57 -0
- package/src/config/safeChainDir.js +71 -0
- package/src/config/settings.js +247 -0
- package/src/environment/environment.js +14 -0
- package/src/environment/userInteraction.js +122 -0
- package/src/installLocation.js +42 -0
- package/src/main.js +123 -0
- package/src/packagemanager/_shared/commandErrors.js +17 -0
- package/src/packagemanager/_shared/matchesCommand.js +18 -0
- package/src/packagemanager/bun/createBunPackageManager.js +48 -0
- package/src/packagemanager/currentPackageManager.js +82 -0
- package/src/packagemanager/npm/createPackageManager.js +72 -0
- package/src/packagemanager/npm/dependencyScanner/commandArgumentScanner.js +74 -0
- package/src/packagemanager/npm/dependencyScanner/nullScanner.js +9 -0
- package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.js +144 -0
- package/src/packagemanager/npm/runNpmCommand.js +20 -0
- package/src/packagemanager/npm/utils/abbrevs-generated.js +359 -0
- package/src/packagemanager/npm/utils/cmd-list.js +174 -0
- package/src/packagemanager/npm/utils/npmCommands.js +34 -0
- package/src/packagemanager/npx/createPackageManager.js +15 -0
- package/src/packagemanager/npx/dependencyScanner/commandArgumentScanner.js +43 -0
- package/src/packagemanager/npx/parsing/parsePackagesFromArguments.js +130 -0
- package/src/packagemanager/npx/runNpxCommand.js +20 -0
- package/src/packagemanager/pip/createPackageManager.js +25 -0
- package/src/packagemanager/pip/pipSettings.js +6 -0
- package/src/packagemanager/pip/runPipCommand.js +209 -0
- package/src/packagemanager/pipx/createPipXPackageManager.js +18 -0
- package/src/packagemanager/pipx/runPipXCommand.js +60 -0
- package/src/packagemanager/pnpm/createPackageManager.js +57 -0
- package/src/packagemanager/pnpm/dependencyScanner/commandArgumentScanner.js +35 -0
- package/src/packagemanager/pnpm/parsing/parsePackagesFromArguments.js +109 -0
- package/src/packagemanager/pnpm/runPnpmCommand.js +32 -0
- package/src/packagemanager/poetry/createPoetryPackageManager.js +72 -0
- package/src/packagemanager/uv/createUvPackageManager.js +18 -0
- package/src/packagemanager/uv/runUvCommand.js +66 -0
- package/src/packagemanager/uvx/createUvxPackageManager.js +18 -0
- package/src/packagemanager/yarn/createPackageManager.js +41 -0
- package/src/packagemanager/yarn/dependencyScanner/commandArgumentScanner.js +35 -0
- package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.js +128 -0
- package/src/packagemanager/yarn/runYarnCommand.js +36 -0
- package/src/registryProxy/certBundle.js +203 -0
- package/src/registryProxy/certUtils.js +178 -0
- package/src/registryProxy/getConnectTimeout.js +13 -0
- package/src/registryProxy/http-utils.js +80 -0
- package/src/registryProxy/interceptors/createInterceptorForEcoSystem.js +25 -0
- package/src/registryProxy/interceptors/interceptorBuilder.js +179 -0
- package/src/registryProxy/interceptors/minimumPackageAgeExclusions.js +33 -0
- package/src/registryProxy/interceptors/npm/modifyNpmInfo.js +180 -0
- package/src/registryProxy/interceptors/npm/npmInterceptor.js +101 -0
- package/src/registryProxy/interceptors/npm/parseNpmPackageUrl.js +60 -0
- 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/isImdsEndpoint.js +13 -0
- package/src/registryProxy/mitmRequestHandler.js +240 -0
- package/src/registryProxy/plainHttpProxy.js +95 -0
- package/src/registryProxy/registryProxy.js +255 -0
- package/src/registryProxy/tunnelRequestHandler.js +213 -0
- package/src/scanning/audit/index.js +129 -0
- package/src/scanning/index.js +82 -0
- package/src/scanning/malwareDatabase.js +131 -0
- 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/helpers.js +296 -0
- package/src/shell-integration/path-wrappers/templates/unix-wrapper.template.sh +37 -0
- package/src/shell-integration/path-wrappers/templates/windows-wrapper.template.cmd +25 -0
- package/src/shell-integration/setup-ci.js +152 -0
- package/src/shell-integration/setup.js +110 -0
- package/src/shell-integration/shellDetection.js +39 -0
- package/src/shell-integration/startup-scripts/init-fish.fish +122 -0
- package/src/shell-integration/startup-scripts/init-posix.sh +112 -0
- package/src/shell-integration/startup-scripts/init-pwsh.ps1 +176 -0
- package/src/shell-integration/supported-shells/bash.js +222 -0
- package/src/shell-integration/supported-shells/fish.js +97 -0
- package/src/shell-integration/supported-shells/powershell.js +102 -0
- package/src/shell-integration/supported-shells/windowsPowershell.js +102 -0
- package/src/shell-integration/supported-shells/zsh.js +94 -0
- package/src/shell-integration/teardown.js +114 -0
- package/src/utils/safeSpawn.js +153 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { ui } from "../environment/userInteraction.js";
|
|
3
|
+
import { detectShells } from "./shellDetection.js";
|
|
4
|
+
import { knownAikidoTools, getPackageManagerList } from "./helpers.js";
|
|
5
|
+
import { getShimsDir, getScriptsDir } from "../config/safeChainDir.js";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
11
|
+
export async function teardown() {
|
|
12
|
+
ui.writeInformation(
|
|
13
|
+
chalk.bold("Removing shell aliases.") +
|
|
14
|
+
` This will remove safe-chain aliases for ${getPackageManagerList()}.`
|
|
15
|
+
);
|
|
16
|
+
ui.emptyLine();
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const shells = detectShells();
|
|
20
|
+
if (shells.length === 0) {
|
|
21
|
+
ui.writeError("No supported shells detected. Cannot remove aliases.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
ui.writeInformation(
|
|
26
|
+
`Detected ${shells.length} supported shell(s): ${shells
|
|
27
|
+
.map((shell) => chalk.bold(shell.name))
|
|
28
|
+
.join(", ")}.`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
let updatedCount = 0;
|
|
32
|
+
for (const shell of shells) {
|
|
33
|
+
let success = false;
|
|
34
|
+
try {
|
|
35
|
+
success = shell.teardown(knownAikidoTools);
|
|
36
|
+
} catch {
|
|
37
|
+
success = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (success) {
|
|
41
|
+
ui.writeInformation(
|
|
42
|
+
`${chalk.bold("- " + shell.name + ":")} ${chalk.green(
|
|
43
|
+
"Teardown successful"
|
|
44
|
+
)}`
|
|
45
|
+
);
|
|
46
|
+
updatedCount++;
|
|
47
|
+
} else {
|
|
48
|
+
ui.writeError(
|
|
49
|
+
`${chalk.bold("- " + shell.name + ":")} ${chalk.red(
|
|
50
|
+
"Teardown failed"
|
|
51
|
+
)}`
|
|
52
|
+
);
|
|
53
|
+
ui.emptyLine();
|
|
54
|
+
ui.writeInformation(` ${chalk.bold("To tear down manually:")}`);
|
|
55
|
+
for (const instruction of shell.getManualTeardownInstructions()) {
|
|
56
|
+
ui.writeInformation(` ${instruction}`);
|
|
57
|
+
}
|
|
58
|
+
ui.emptyLine();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (updatedCount > 0) {
|
|
63
|
+
ui.emptyLine();
|
|
64
|
+
ui.writeInformation(`Please restart your terminal to apply the changes.`);
|
|
65
|
+
}
|
|
66
|
+
} catch (/** @type {any} */ error) {
|
|
67
|
+
ui.writeError(
|
|
68
|
+
`Failed to remove shell aliases: ${error.message}. Please check your shell configuration.`
|
|
69
|
+
);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Removes directories created by setup-ci and setup commands
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
*/
|
|
78
|
+
export async function teardownDirectories() {
|
|
79
|
+
const shimsDir = getShimsDir();
|
|
80
|
+
const scriptsDir = getScriptsDir();
|
|
81
|
+
|
|
82
|
+
// Remove CI shims directory
|
|
83
|
+
if (fs.existsSync(shimsDir)) {
|
|
84
|
+
try {
|
|
85
|
+
fs.rmSync(shimsDir, { recursive: true, force: true });
|
|
86
|
+
ui.writeInformation(
|
|
87
|
+
`${chalk.bold("- CI Shims:")} ${chalk.green("Removed successfully")}`
|
|
88
|
+
);
|
|
89
|
+
} catch (/** @type {any} */ error) {
|
|
90
|
+
ui.writeError(
|
|
91
|
+
`${chalk.bold("- CI Shims:")} ${chalk.red(
|
|
92
|
+
"Failed to remove"
|
|
93
|
+
)}. Error: ${error.message}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Remove scripts directory
|
|
99
|
+
if (fs.existsSync(scriptsDir)) {
|
|
100
|
+
try {
|
|
101
|
+
fs.rmSync(scriptsDir, { recursive: true, force: true });
|
|
102
|
+
ui.writeInformation(
|
|
103
|
+
`${chalk.bold("- Scripts:")} ${chalk.green("Removed successfully")}`
|
|
104
|
+
);
|
|
105
|
+
} catch (/** @type {any} */ error) {
|
|
106
|
+
ui.writeError(
|
|
107
|
+
`${chalk.bold("- Scripts:")} ${chalk.red(
|
|
108
|
+
"Failed to remove"
|
|
109
|
+
)}. Error: ${error.message}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { spawn, execSync } from "child_process";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import { ui } from "../environment/userInteraction.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} arg
|
|
7
|
+
*
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
function sanitizeShellArgument(arg) {
|
|
11
|
+
// If argument contains shell metacharacters, wrap in double quotes
|
|
12
|
+
// and escape characters that are special even inside double quotes
|
|
13
|
+
if (hasShellMetaChars(arg)) {
|
|
14
|
+
// Inside double quotes, we need to escape: " $ ` \
|
|
15
|
+
return '"' + escapeDoubleQuoteContent(arg) + '"';
|
|
16
|
+
}
|
|
17
|
+
return arg;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} arg
|
|
22
|
+
*
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
function hasShellMetaChars(arg) {
|
|
26
|
+
// Shell metacharacters that need escaping
|
|
27
|
+
// These characters have special meaning in shells and need to be quoted
|
|
28
|
+
// Whenever one of these characters is present, we should quote the argument
|
|
29
|
+
// Characters: space, ", &, ', |, ;, <, >, (, ), $, `, \, !, *, ?, [, ], {, }, ~, #
|
|
30
|
+
const shellMetaChars = /[ "&'|;<>()$`\\!*?[\]{}~#]/;
|
|
31
|
+
return shellMetaChars.test(arg);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} arg
|
|
36
|
+
*
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function escapeDoubleQuoteContent(arg) {
|
|
40
|
+
// Escape special characters for shell safety
|
|
41
|
+
// This escapes ", $, `, and \ by prefixing them with a backslash
|
|
42
|
+
return arg.replace(/(["`$\\])/g, "\\$1");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} command
|
|
47
|
+
* @param {string[]} args
|
|
48
|
+
*
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function buildCommand(command, args) {
|
|
52
|
+
if (args.length === 0) {
|
|
53
|
+
return command;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const escapedArgs = args.map(sanitizeShellArgument);
|
|
57
|
+
|
|
58
|
+
return `${command} ${escapedArgs.join(" ")}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {string} command
|
|
63
|
+
*
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
function resolveCommandPath(command) {
|
|
67
|
+
// command will be "npm", "yarn", etc.
|
|
68
|
+
// Use 'command -v' to find the full path
|
|
69
|
+
const fullPath = execSync(`command -v ${command}`, {
|
|
70
|
+
encoding: "utf8",
|
|
71
|
+
}).trim();
|
|
72
|
+
|
|
73
|
+
if (!fullPath) {
|
|
74
|
+
throw new Error(`Command not found: ${command}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return fullPath;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} command
|
|
82
|
+
* @param {string[]} args
|
|
83
|
+
* @param {import("child_process").SpawnOptions} options
|
|
84
|
+
*
|
|
85
|
+
* @returns {Promise<{status: number, stdout: string, stderr: string}>}
|
|
86
|
+
*/
|
|
87
|
+
export async function safeSpawn(command, args, options = {}) {
|
|
88
|
+
// The command is always one of our supported package managers.
|
|
89
|
+
// It should always be alphanumeric or _ or -
|
|
90
|
+
// Reject any command names with suspicious characters
|
|
91
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(command)) {
|
|
92
|
+
throw new Error(`Invalid command name: ${command}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
// Windows requires shell: true because .bat and .cmd files are not executable
|
|
97
|
+
// without a terminal. On Unix/macOS, we resolve the full path first, then use
|
|
98
|
+
// array args (safer, no escaping needed).
|
|
99
|
+
// See: https://nodejs.org/api/child_process.html#child_processspawncommand-args-options
|
|
100
|
+
let child;
|
|
101
|
+
if (os.platform() === "win32") {
|
|
102
|
+
const fullCommand = buildCommand(command, args);
|
|
103
|
+
child = spawn(fullCommand, { ...options, shell: true });
|
|
104
|
+
} else {
|
|
105
|
+
const fullPath = resolveCommandPath(command);
|
|
106
|
+
child = spawn(fullPath, args, options);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// When stdio is piped, we need to collect the output
|
|
110
|
+
let stdout = "";
|
|
111
|
+
let stderr = "";
|
|
112
|
+
|
|
113
|
+
child.stdout?.on("data", (data) => {
|
|
114
|
+
stdout += data.toString();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
child.stderr?.on("data", (data) => {
|
|
118
|
+
stderr += data.toString();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
child.on("close", (code) => {
|
|
122
|
+
// Code is null if it terminated by a signal. This should never
|
|
123
|
+
// happen in our code. If this happens, return 1 error code.
|
|
124
|
+
|
|
125
|
+
code = code ?? 1;
|
|
126
|
+
|
|
127
|
+
resolve({
|
|
128
|
+
status: code,
|
|
129
|
+
stdout: stdout,
|
|
130
|
+
stderr: stderr,
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
child.on("error", (error) => {
|
|
135
|
+
reject(error);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {string} command
|
|
142
|
+
* @param {string[]} args
|
|
143
|
+
* @param {import("child_process").SpawnOptions} options
|
|
144
|
+
*
|
|
145
|
+
* @returns {Promise<{status: number, stdout: string, stderr: string}>}
|
|
146
|
+
*/
|
|
147
|
+
export async function printVerboseAndSafeSpawn(command, args, options = {}) {
|
|
148
|
+
ui.writeVerbose(`Running: ${command} ${args.join(" ")}`);
|
|
149
|
+
|
|
150
|
+
const result = await safeSpawn(command, args, options);
|
|
151
|
+
|
|
152
|
+
return result;
|
|
153
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["es2023"],
|
|
4
|
+
"module": "node16",
|
|
5
|
+
"strict": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"moduleResolution": "node16",
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"checkJs": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"resolveJsonModule": true
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"src/**/*.js",
|
|
15
|
+
"bin/**/*.js"
|
|
16
|
+
],
|
|
17
|
+
"exclude": [
|
|
18
|
+
"node_modules",
|
|
19
|
+
"src/**/*.spec.js"
|
|
20
|
+
]
|
|
21
|
+
}
|