@aikidosec/safe-chain 1.0.0 → 1.0.10
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/.editorconfig +8 -0
- package/.github/workflows/build-and-release.yml +41 -0
- package/.github/workflows/test-on-pr.yml +28 -0
- package/README.md +55 -0
- package/bin/aikido-npm.js +8 -0
- package/bin/aikido-npx.js +8 -0
- package/bin/aikido-yarn.js +8 -0
- package/eslint.config.js +25 -0
- package/package.json +27 -5
- package/safe-package-manager-demo.gif +0 -0
- package/src/api/aikido.js +31 -0
- package/src/api/npmApi.js +46 -0
- package/src/config/configFile.js +91 -0
- package/src/environment/environment.js +14 -0
- package/src/environment/userInteraction.js +79 -0
- package/src/main.js +31 -0
- package/src/packagemanager/currentPackageManager.js +28 -0
- package/src/packagemanager/npm/createPackageManager.js +83 -0
- package/src/packagemanager/npm/dependencyScanner/commandArgumentScanner.js +37 -0
- package/src/packagemanager/npm/dependencyScanner/dryRunScanner.js +50 -0
- package/src/packagemanager/npm/dependencyScanner/nullScanner.js +6 -0
- package/src/packagemanager/npm/parsing/parseNpmInstallDryRunOutput.js +57 -0
- package/src/packagemanager/npm/parsing/parseNpmInstallDryRunOutput.spec.js +134 -0
- package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.js +109 -0
- package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.spec.js +176 -0
- package/src/packagemanager/npm/runNpmCommand.js +33 -0
- package/src/packagemanager/npm/utils/cmd-list.js +171 -0
- package/src/packagemanager/npm/utils/npmCommands.js +26 -0
- package/src/packagemanager/npx/createPackageManager.js +13 -0
- package/src/packagemanager/npx/dependencyScanner/commandArgumentScanner.js +31 -0
- package/src/packagemanager/npx/parsing/parsePackagesFromArguments.js +106 -0
- package/src/packagemanager/npx/parsing/parsePackagesFromArguments.spec.js +147 -0
- package/src/packagemanager/npx/runNpxCommand.js +17 -0
- package/src/packagemanager/yarn/createPackageManager.js +34 -0
- package/src/packagemanager/yarn/dependencyScanner/commandArgumentScanner.js +28 -0
- package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.js +102 -0
- package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.spec.js +126 -0
- package/src/packagemanager/yarn/runYarnCommand.js +17 -0
- package/src/scanning/audit/index.js +56 -0
- package/src/scanning/index.js +94 -0
- package/src/scanning/index.scanCommand.spec.js +180 -0
- package/src/scanning/index.shouldScanCommand.spec.js +47 -0
- package/src/scanning/malwareDatabase.js +62 -0
- package/src/shell-integration/addAlias.js +63 -0
- package/src/shell-integration/helpers.js +44 -0
- package/src/shell-integration/removeAlias.js +61 -0
- package/src/shell-integration/shellIntegration.spec.js +172 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getAliases } from "./helpers.js";
|
|
4
|
+
import { ui } from "../environment/userInteraction.js";
|
|
5
|
+
import { EOL } from "os";
|
|
6
|
+
|
|
7
|
+
export function isRemoveAliasCommand(args) {
|
|
8
|
+
if (args[0] === "remove-aikido-aliases") {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (args[0] === "remove-aikido-npm-alias") {
|
|
13
|
+
// not in the documenation anymore, but still here for backwards compatibility
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function removeAlias(args) {
|
|
21
|
+
if (!isRemoveAliasCommand(args)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (args.length < 2) {
|
|
26
|
+
ui.writeError("Please specify the file to remove the alias from.");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const filePath = path.resolve(args[1]);
|
|
31
|
+
const aliases = getAliases(filePath);
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(filePath)) {
|
|
34
|
+
ui.writeError(`File ${filePath} does not exist.`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const alias of aliases) {
|
|
39
|
+
const fileContent = fs.readFileSync(filePath, "utf-8");
|
|
40
|
+
if (!fileContent.includes(alias)) {
|
|
41
|
+
ui.writeInformation(`Alias "${alias}" does not exist in ${filePath}`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ui.writeInformation(`Removing alias "${alias}" from ${filePath}...`);
|
|
46
|
+
|
|
47
|
+
// Remove the alias from the file.
|
|
48
|
+
// Make sure to remove the added newlines as well, but also make sure to remove aliases if they don't have newlines
|
|
49
|
+
const updatedContent = fileContent
|
|
50
|
+
.replaceAll(`${EOL}${alias}${EOL}`, "")
|
|
51
|
+
.replaceAll(`${alias}${EOL}`, "")
|
|
52
|
+
.replaceAll(`${EOL}${alias}`, "")
|
|
53
|
+
.replaceAll(alias, "");
|
|
54
|
+
|
|
55
|
+
fs.writeFileSync(filePath, updatedContent, "utf-8");
|
|
56
|
+
ui.writeInformation(`Alias "${alias}" removed from ${filePath}`);
|
|
57
|
+
}
|
|
58
|
+
ui.writeInformation(
|
|
59
|
+
`Please restart your terminal for the changes to take effect.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { EOL, tmpdir } from "node:os";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { addAlias } from "./addAlias.js";
|
|
6
|
+
import { removeAlias } from "./removeAlias.js";
|
|
7
|
+
|
|
8
|
+
describe("Add alias", () => {
|
|
9
|
+
function runTestsForEnvironment(shell, startupExtension, aliases) {
|
|
10
|
+
it(`should add alias to ${shell} file`, () => {
|
|
11
|
+
const lines = [`#!/usr/bin/env ${shell}`, "", "alias cls='clear'"];
|
|
12
|
+
const filePath = createShellStartupScript(lines, startupExtension);
|
|
13
|
+
|
|
14
|
+
addAlias(["add-aikido-aliases", filePath]);
|
|
15
|
+
|
|
16
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
17
|
+
for (const alias of aliases) {
|
|
18
|
+
assert.ok(fileContent.includes(alias));
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it(`should not add alias if it already exists in ${shell} file`, () => {
|
|
23
|
+
const lines = [`#!/usr/bin/env ${shell}`, "", ...aliases];
|
|
24
|
+
const filePath = createShellStartupScript(lines, startupExtension);
|
|
25
|
+
|
|
26
|
+
addAlias(["add-aikido-aliases", filePath]);
|
|
27
|
+
|
|
28
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
29
|
+
assert.strictEqual(fileContent.length, lines.length);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it(`should create file and add alias if file does not exist`, () => {
|
|
33
|
+
const filePath = `${tmpdir()}/nonexistent-file${startupExtension}`;
|
|
34
|
+
if (fs.existsSync(filePath)) {
|
|
35
|
+
fs.rmSync(filePath, { force: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
addAlias(["add-aikido-aliases", filePath]);
|
|
39
|
+
|
|
40
|
+
assert.ok(fs.existsSync(filePath));
|
|
41
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
42
|
+
for (const alias of aliases) {
|
|
43
|
+
assert.ok(fileContent.includes(alias));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it(`should add the alias only once in ${shell} file`, () => {
|
|
48
|
+
const lines = [`#!/usr/bin/env ${shell}`, ""];
|
|
49
|
+
const filePath = createShellStartupScript(lines, startupExtension);
|
|
50
|
+
|
|
51
|
+
addAlias(["add-aikido-aliases", filePath]);
|
|
52
|
+
addAlias(["add-aikido-aliases", filePath]);
|
|
53
|
+
|
|
54
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
55
|
+
for (const alias of aliases) {
|
|
56
|
+
assert.strictEqual(countOccurences(fileContent, alias), 1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it(`should remove one alias from ${shell} file`, () => {
|
|
61
|
+
const alias = aliases[Math.floor(Math.random() * aliases.length)];
|
|
62
|
+
const lines = [`#!/usr/bin/env ${shell}`, "", alias];
|
|
63
|
+
const filePath = createShellStartupScript(lines, startupExtension);
|
|
64
|
+
|
|
65
|
+
removeAlias(["remove-aikido-npm-alias", filePath]);
|
|
66
|
+
|
|
67
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
68
|
+
assert.ok(!fileContent.includes(alias));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it(`should remove all aliases from ${shell} file`, () => {
|
|
72
|
+
const lines = [`#!/usr/bin/env ${shell}`, "", ...aliases, ""];
|
|
73
|
+
const filePath = createShellStartupScript(lines, startupExtension);
|
|
74
|
+
|
|
75
|
+
removeAlias(["remove-aikido-npm-alias", filePath]);
|
|
76
|
+
|
|
77
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
78
|
+
for (const alias of aliases) {
|
|
79
|
+
assert.ok(!fileContent.includes(alias));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it(`should not remove alias if it does not exist in ${shell} file`, () => {
|
|
84
|
+
const lines = [`#!/usr/bin/env ${shell}`, "", "alias cls='clear'"];
|
|
85
|
+
const filePath = createShellStartupScript(lines, startupExtension);
|
|
86
|
+
|
|
87
|
+
removeAlias(["remove-aikido-npm-alias", filePath]);
|
|
88
|
+
|
|
89
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
90
|
+
assert.ok(fileContent.includes("alias cls='clear'"));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it(`should not remove alias if file does not exist`, () => {
|
|
94
|
+
const filePath = `${tmpdir()}/nonexistent-file${startupExtension}`;
|
|
95
|
+
if (fs.existsSync(filePath)) {
|
|
96
|
+
fs.rmSync(filePath, { force: true });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
removeAlias(["remove-aikido-npm-alias", filePath]);
|
|
100
|
+
|
|
101
|
+
assert.ok(!fs.existsSync(filePath));
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it(`should remove aliases from ${shell} file if duplicated`, () => {
|
|
105
|
+
const lines = [
|
|
106
|
+
`#!/usr/bin/env ${shell}`,
|
|
107
|
+
"",
|
|
108
|
+
...aliases,
|
|
109
|
+
"cls='clear'",
|
|
110
|
+
...aliases,
|
|
111
|
+
...aliases,
|
|
112
|
+
"",
|
|
113
|
+
];
|
|
114
|
+
const filePath = createShellStartupScript(lines, startupExtension);
|
|
115
|
+
|
|
116
|
+
removeAlias(["remove-aikido-npm-alias", filePath]);
|
|
117
|
+
|
|
118
|
+
const fileContent = readAndDeleteFile(filePath);
|
|
119
|
+
for (const alias of aliases) {
|
|
120
|
+
assert.ok(!fileContent.includes(alias));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
runTestsForEnvironment("bash", ".bashrc", [
|
|
126
|
+
"alias npm='aikido-npm'",
|
|
127
|
+
"alias npx='aikido-npx'",
|
|
128
|
+
"alias yarn='aikido-yarn'",
|
|
129
|
+
]);
|
|
130
|
+
runTestsForEnvironment("zsh", ".zshrc", [
|
|
131
|
+
"alias npm='aikido-npm'",
|
|
132
|
+
"alias npx='aikido-npx'",
|
|
133
|
+
"alias yarn='aikido-yarn'",
|
|
134
|
+
]);
|
|
135
|
+
runTestsForEnvironment("fish", ".fish", [
|
|
136
|
+
'alias npm "aikido-npm"',
|
|
137
|
+
'alias npx "aikido-npx"',
|
|
138
|
+
'alias yarn "aikido-yarn"',
|
|
139
|
+
]);
|
|
140
|
+
runTestsForEnvironment("pwsh", ".ps1", [
|
|
141
|
+
"Set-Alias npm aikido-npm",
|
|
142
|
+
"Set-Alias npx aikido-npx",
|
|
143
|
+
"Set-Alias yarn aikido-yarn",
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
function createShellStartupScript(lines, fileExtension) {
|
|
148
|
+
var randomFileName = Math.random().toString(36).substring(2, 15);
|
|
149
|
+
var filePath = `${tmpdir()}/${randomFileName}${fileExtension}`;
|
|
150
|
+
fs.writeFileSync(filePath, lines.join(EOL), "utf-8");
|
|
151
|
+
return filePath;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function readAndDeleteFile(filePath) {
|
|
155
|
+
var fileContent = fs.readFileSync(filePath, "utf-8");
|
|
156
|
+
fs.rm(filePath, { force: true }, (err) => {
|
|
157
|
+
if (err) {
|
|
158
|
+
console.error(`Error deleting file: ${err}`);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return fileContent.split(EOL);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function countOccurences(lines, alias) {
|
|
165
|
+
let count = 0;
|
|
166
|
+
for (const line of lines) {
|
|
167
|
+
if (line.includes(alias)) {
|
|
168
|
+
count++;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return count;
|
|
172
|
+
}
|