@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.
Files changed (47) hide show
  1. package/.editorconfig +8 -0
  2. package/.github/workflows/build-and-release.yml +41 -0
  3. package/.github/workflows/test-on-pr.yml +28 -0
  4. package/README.md +55 -0
  5. package/bin/aikido-npm.js +8 -0
  6. package/bin/aikido-npx.js +8 -0
  7. package/bin/aikido-yarn.js +8 -0
  8. package/eslint.config.js +25 -0
  9. package/package.json +27 -5
  10. package/safe-package-manager-demo.gif +0 -0
  11. package/src/api/aikido.js +31 -0
  12. package/src/api/npmApi.js +46 -0
  13. package/src/config/configFile.js +91 -0
  14. package/src/environment/environment.js +14 -0
  15. package/src/environment/userInteraction.js +79 -0
  16. package/src/main.js +31 -0
  17. package/src/packagemanager/currentPackageManager.js +28 -0
  18. package/src/packagemanager/npm/createPackageManager.js +83 -0
  19. package/src/packagemanager/npm/dependencyScanner/commandArgumentScanner.js +37 -0
  20. package/src/packagemanager/npm/dependencyScanner/dryRunScanner.js +50 -0
  21. package/src/packagemanager/npm/dependencyScanner/nullScanner.js +6 -0
  22. package/src/packagemanager/npm/parsing/parseNpmInstallDryRunOutput.js +57 -0
  23. package/src/packagemanager/npm/parsing/parseNpmInstallDryRunOutput.spec.js +134 -0
  24. package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.js +109 -0
  25. package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.spec.js +176 -0
  26. package/src/packagemanager/npm/runNpmCommand.js +33 -0
  27. package/src/packagemanager/npm/utils/cmd-list.js +171 -0
  28. package/src/packagemanager/npm/utils/npmCommands.js +26 -0
  29. package/src/packagemanager/npx/createPackageManager.js +13 -0
  30. package/src/packagemanager/npx/dependencyScanner/commandArgumentScanner.js +31 -0
  31. package/src/packagemanager/npx/parsing/parsePackagesFromArguments.js +106 -0
  32. package/src/packagemanager/npx/parsing/parsePackagesFromArguments.spec.js +147 -0
  33. package/src/packagemanager/npx/runNpxCommand.js +17 -0
  34. package/src/packagemanager/yarn/createPackageManager.js +34 -0
  35. package/src/packagemanager/yarn/dependencyScanner/commandArgumentScanner.js +28 -0
  36. package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.js +102 -0
  37. package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.spec.js +126 -0
  38. package/src/packagemanager/yarn/runYarnCommand.js +17 -0
  39. package/src/scanning/audit/index.js +56 -0
  40. package/src/scanning/index.js +94 -0
  41. package/src/scanning/index.scanCommand.spec.js +180 -0
  42. package/src/scanning/index.shouldScanCommand.spec.js +47 -0
  43. package/src/scanning/malwareDatabase.js +62 -0
  44. package/src/shell-integration/addAlias.js +63 -0
  45. package/src/shell-integration/helpers.js +44 -0
  46. package/src/shell-integration/removeAlias.js +61 -0
  47. package/src/shell-integration/shellIntegration.spec.js +172 -0
@@ -0,0 +1,171 @@
1
+ // Based on https://github.com/npm/cli/blob/latest/lib/utils/cmd-list.js
2
+
3
+ import abbrev from "abbrev";
4
+
5
+ const commands = [
6
+ "access",
7
+ "adduser",
8
+ "audit",
9
+ "bugs",
10
+ "cache",
11
+ "ci",
12
+ "completion",
13
+ "config",
14
+ "dedupe",
15
+ "deprecate",
16
+ "diff",
17
+ "dist-tag",
18
+ "docs",
19
+ "doctor",
20
+ "edit",
21
+ "exec",
22
+ "explain",
23
+ "explore",
24
+ "find-dupes",
25
+ "fund",
26
+ "get",
27
+ "help",
28
+ "help-search",
29
+ "init",
30
+ "install",
31
+ "install-ci-test",
32
+ "install-test",
33
+ "link",
34
+ "ll",
35
+ "login",
36
+ "logout",
37
+ "ls",
38
+ "org",
39
+ "outdated",
40
+ "owner",
41
+ "pack",
42
+ "ping",
43
+ "pkg",
44
+ "prefix",
45
+ "profile",
46
+ "prune",
47
+ "publish",
48
+ "query",
49
+ "rebuild",
50
+ "repo",
51
+ "restart",
52
+ "root",
53
+ "run",
54
+ "sbom",
55
+ "search",
56
+ "set",
57
+ "shrinkwrap",
58
+ "star",
59
+ "stars",
60
+ "start",
61
+ "stop",
62
+ "team",
63
+ "test",
64
+ "token",
65
+ "undeprecate",
66
+ "uninstall",
67
+ "unpublish",
68
+ "unstar",
69
+ "update",
70
+ "version",
71
+ "view",
72
+ "whoami",
73
+ ];
74
+
75
+ // These must resolve to an entry in commands
76
+ const aliases = {
77
+ // aliases
78
+ author: "owner",
79
+ home: "docs",
80
+ issues: "bugs",
81
+ info: "view",
82
+ show: "view",
83
+ find: "search",
84
+ add: "install",
85
+ unlink: "uninstall",
86
+ remove: "uninstall",
87
+ rm: "uninstall",
88
+ r: "uninstall",
89
+
90
+ // short names for common things
91
+ un: "uninstall",
92
+ rb: "rebuild",
93
+ list: "ls",
94
+ ln: "link",
95
+ create: "init",
96
+ i: "install",
97
+ it: "install-test",
98
+ cit: "install-ci-test",
99
+ up: "update",
100
+ c: "config",
101
+ s: "search",
102
+ se: "search",
103
+ tst: "test",
104
+ t: "test",
105
+ ddp: "dedupe",
106
+ v: "view",
107
+ "run-script": "run",
108
+ "clean-install": "ci",
109
+ "clean-install-test": "install-ci-test",
110
+ x: "exec",
111
+ why: "explain",
112
+ la: "ll",
113
+ verison: "version",
114
+ ic: "ci",
115
+
116
+ // typos
117
+ innit: "init",
118
+ // manually abbrev so that install-test doesn't make insta stop working
119
+ in: "install",
120
+ ins: "install",
121
+ inst: "install",
122
+ insta: "install",
123
+ instal: "install",
124
+ isnt: "install",
125
+ isnta: "install",
126
+ isntal: "install",
127
+ isntall: "install",
128
+ "install-clean": "ci",
129
+ "isntall-clean": "ci",
130
+ hlep: "help",
131
+ "dist-tags": "dist-tag",
132
+ upgrade: "update",
133
+ udpate: "update",
134
+ rum: "run",
135
+ sit: "install-ci-test",
136
+ urn: "run",
137
+ ogr: "org",
138
+ "add-user": "adduser",
139
+ };
140
+
141
+ export function deref(c) {
142
+ if (!c) {
143
+ return;
144
+ }
145
+
146
+ // Translate camelCase to snake-case (i.e. installTest to install-test)
147
+ if (c.match(/[A-Z]/)) {
148
+ c = c.replace(/([A-Z])/g, (m) => "-" + m.toLowerCase());
149
+ }
150
+
151
+ // if they asked for something exactly we are done
152
+ if (commands.includes(c)) {
153
+ return c;
154
+ }
155
+
156
+ // if they asked for a direct alias
157
+ if (aliases[c]) {
158
+ return aliases[c];
159
+ }
160
+
161
+ const abbrevs = abbrev(commands.concat(Object.keys(aliases)));
162
+
163
+ // first deref the abbrev, if there is one
164
+ // then resolve any aliases
165
+ // so `npm install-cl` will resolve to `install-clean` then to `ci`
166
+ let a = abbrevs[c];
167
+ while (aliases[a]) {
168
+ a = aliases[a];
169
+ }
170
+ return a;
171
+ }
@@ -0,0 +1,26 @@
1
+ import { deref } from "./cmd-list.js";
2
+
3
+ export function getNpmCommandForArgs(args) {
4
+ if (args.length === 0) {
5
+ return null;
6
+ }
7
+
8
+ const argCommand = deref(args[0]);
9
+ if (!argCommand) {
10
+ return null;
11
+ }
12
+
13
+ return argCommand;
14
+ }
15
+
16
+ export function hasDryRunArg(args) {
17
+ return args.some((arg) => arg === "--dry-run");
18
+ }
19
+
20
+ export const npmInstallCommand = "install";
21
+ export const npmCiCommand = "ci";
22
+ export const npmInstallTestCommand = "install-test";
23
+ export const npmInstallCiTestCommand = "install-ci-test";
24
+ export const npmUpdateCommand = "update";
25
+ export const npmAuditCommand = "audit";
26
+ export const npmExecCommand = "exec";
@@ -0,0 +1,13 @@
1
+ import { commandArgumentScanner } from "./dependencyScanner/commandArgumentScanner.js";
2
+ import { runNpx } from "./runNpxCommand.js";
3
+
4
+ export function createNpxPackageManager() {
5
+ const scanner = commandArgumentScanner();
6
+
7
+ return {
8
+ getWarningMessage: () => null,
9
+ runCommand: runNpx,
10
+ isSupportedCommand: (args) => scanner.shouldScan(args),
11
+ getDependencyUpdatesForCommand: (args) => scanner.scan(args),
12
+ };
13
+ }
@@ -0,0 +1,31 @@
1
+ import { resolvePackageVersion } from "../../../api/npmApi.js";
2
+ import { parsePackagesFromArguments } from "../parsing/parsePackagesFromArguments.js";
3
+
4
+ export function commandArgumentScanner() {
5
+ return {
6
+ scan: (args) => scanDependencies(args),
7
+ shouldScan: () => true, // all npx commands need to be scanned, npx doesn't have dry-run
8
+ };
9
+ }
10
+ function scanDependencies(args) {
11
+ return checkChangesFromArgs(args);
12
+ }
13
+
14
+ export async function checkChangesFromArgs(args) {
15
+ const changes = [];
16
+ const packageUpdates = parsePackagesFromArguments(args);
17
+
18
+ for (const packageUpdate of packageUpdates) {
19
+ var exactVersion = await resolvePackageVersion(
20
+ packageUpdate.name,
21
+ packageUpdate.version
22
+ );
23
+ if (exactVersion) {
24
+ packageUpdate.version = exactVersion;
25
+ }
26
+
27
+ changes.push({ ...packageUpdate, type: "add" });
28
+ }
29
+
30
+ return changes;
31
+ }
@@ -0,0 +1,106 @@
1
+ export function parsePackagesFromArguments(args) {
2
+ let defaultTag = "latest";
3
+
4
+ for (let i = 0; i < args.length; i++) {
5
+ const arg = args[i];
6
+ const option = getOption(arg);
7
+
8
+ if (option) {
9
+ // If the option has a parameter, skip the next argument as well
10
+ i += option.numberOfParameters;
11
+
12
+ continue;
13
+ }
14
+
15
+ const packageDetails = parsePackagename(arg, defaultTag);
16
+ if (packageDetails) {
17
+ return [packageDetails];
18
+ }
19
+ }
20
+
21
+ return [];
22
+ }
23
+
24
+ function getOption(arg) {
25
+ if (isOptionWithParameter(arg)) {
26
+ return {
27
+ name: arg,
28
+ numberOfParameters: 1,
29
+ };
30
+ }
31
+
32
+ // Arguments starting with "-" or "--" are considered options
33
+ // except for "--package=" which contains the package name
34
+ if (arg.startsWith("-") && !arg.startsWith("--package=")) {
35
+ return {
36
+ name: arg,
37
+ numberOfParameters: 0,
38
+ };
39
+ }
40
+
41
+ return undefined;
42
+ }
43
+
44
+ function isOptionWithParameter(arg) {
45
+ const optionsWithParameters = [
46
+ "--access",
47
+ "--auth-type",
48
+ "--cache",
49
+ "--fetch-retries",
50
+ "--fetch-retry-mintimeout",
51
+ "--fetch-retry-maxtimeout",
52
+ "--fetch-retry-factor",
53
+ "--fetch-timeout",
54
+ "--https-proxy",
55
+ "--include",
56
+ "--location",
57
+ "--lockfile-version",
58
+ "--loglevel",
59
+ "--omit",
60
+ "--proxy",
61
+ "--registry",
62
+ "--replace-registry-host",
63
+ "--tag",
64
+ "--user-config",
65
+ "--workspace",
66
+ ];
67
+
68
+ return optionsWithParameters.includes(arg);
69
+ }
70
+
71
+ function parsePackagename(arg, defaultTag) {
72
+ // format can be --package=name@version
73
+ // in that case, we need to remove the --package= part
74
+ if (arg.startsWith("--package=")) {
75
+ arg = arg.slice(10);
76
+ }
77
+
78
+ arg = removeAlias(arg);
79
+
80
+ // Split at the last "@" to separate the package name and version
81
+ const lastAtIndex = arg.lastIndexOf("@");
82
+
83
+ let name, version;
84
+ if (lastAtIndex !== -1) {
85
+ name = arg.slice(0, lastAtIndex);
86
+ version = arg.slice(lastAtIndex + 1);
87
+ } else {
88
+ name = arg;
89
+ version = defaultTag; // No tag specified (eg: "http-server"), use the default tag
90
+ }
91
+
92
+ return {
93
+ name,
94
+ version,
95
+ };
96
+ }
97
+
98
+ function removeAlias(arg) {
99
+ // removes the alias.
100
+ // Eg.: server@npm:http-server@latest becomes http-server@latest
101
+ const aliasIndex = arg.indexOf("@npm:");
102
+ if (aliasIndex !== -1) {
103
+ return arg.slice(aliasIndex + 5);
104
+ }
105
+ return arg;
106
+ }
@@ -0,0 +1,147 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert";
3
+ import { parsePackagesFromArguments } from "./parsePackagesFromArguments.js";
4
+
5
+ describe("parsePackagesFromArguments", () => {
6
+ it("should return an empty array for no changes", () => {
7
+ const args = [];
8
+
9
+ const result = parsePackagesFromArguments(args);
10
+
11
+ assert.deepEqual(result, []);
12
+ });
13
+
14
+ it("should return an array of changes for one package", () => {
15
+ const args = ["http-server@14.1.1"];
16
+
17
+ const result = parsePackagesFromArguments(args);
18
+
19
+ assert.deepEqual(result, [{ name: "http-server", version: "14.1.1" }]);
20
+ });
21
+
22
+ it("should return the package with latest tag if absent", () => {
23
+ const args = ["http-server"];
24
+
25
+ const result = parsePackagesFromArguments(args);
26
+
27
+ assert.deepEqual(result, [{ name: "http-server", version: "latest" }]);
28
+ });
29
+
30
+ it("should ignore double --", () => {
31
+ const args = ["--", "http-server"];
32
+
33
+ const result = parsePackagesFromArguments(args);
34
+
35
+ assert.deepEqual(result, [{ name: "http-server", version: "latest" }]);
36
+ });
37
+
38
+ it("should only return the first package", () => {
39
+ const args = ["http-server", "jest"];
40
+
41
+ const result = parsePackagesFromArguments(args);
42
+
43
+ assert.deepEqual(result, [{ name: "http-server", version: "latest" }]);
44
+ });
45
+
46
+ it("should return package with -p option", () => {
47
+ const args = ["-p", "http-server"];
48
+
49
+ const result = parsePackagesFromArguments(args);
50
+
51
+ assert.deepEqual(result, [{ name: "http-server", version: "latest" }]);
52
+ });
53
+
54
+ it("should return package with --package option", () => {
55
+ const args = ["--package", "http-server"];
56
+
57
+ const result = parsePackagesFromArguments(args);
58
+
59
+ assert.deepEqual(result, [{ name: "http-server", version: "latest" }]);
60
+ });
61
+
62
+ it("should return package with --package=x option", () => {
63
+ const args = ["--package=http-server"];
64
+
65
+ const result = parsePackagesFromArguments(args);
66
+
67
+ assert.deepEqual(result, [{ name: "http-server", version: "latest" }]);
68
+ });
69
+
70
+ it("should return package with --package=x@version option", () => {
71
+ const args = ["--package=http-server@1.0.0"];
72
+
73
+ const result = parsePackagesFromArguments(args);
74
+
75
+ assert.deepEqual(result, [{ name: "http-server", version: "1.0.0" }]);
76
+ });
77
+
78
+ it("should ignore options with parameters and return an array of changes", () => {
79
+ const args = ["--loglevel", "error", "http-server@14.1.1"];
80
+
81
+ const result = parsePackagesFromArguments(args);
82
+
83
+ assert.deepEqual(result, [{ name: "http-server", version: "14.1.1" }]);
84
+ });
85
+
86
+ it("should parse version even for aliased packages", () => {
87
+ const args = ["server@npm:http-server@14.1.1"];
88
+
89
+ const result = parsePackagesFromArguments(args);
90
+
91
+ assert.deepEqual(result, [{ name: "http-server", version: "14.1.1" }]);
92
+ });
93
+
94
+ it("should parse scoped packages", () => {
95
+ const args = ["@scope/package@1.0.0"];
96
+
97
+ const result = parsePackagesFromArguments(args);
98
+
99
+ assert.deepEqual(result, [{ name: "@scope/package", version: "1.0.0" }]);
100
+ });
101
+
102
+ it("should parse packages with version ranges", () => {
103
+ const args = ["http-server@^14.1.1"];
104
+
105
+ const result = parsePackagesFromArguments(args);
106
+
107
+ assert.deepEqual(result, [{ name: "http-server", version: "^14.1.1" }]);
108
+ });
109
+
110
+ it("should parse package folders", () => {
111
+ const args = ["./local-package"];
112
+
113
+ const result = parsePackagesFromArguments(args);
114
+
115
+ assert.deepEqual(result, [{ name: "./local-package", version: "latest" }]);
116
+ });
117
+
118
+ it("should parse tarballs", () => {
119
+ const args = ["file:./local-package.tgz"];
120
+
121
+ const result = parsePackagesFromArguments(args);
122
+
123
+ assert.deepEqual(result, [
124
+ { name: "file:./local-package.tgz", version: "latest" },
125
+ ]);
126
+ });
127
+
128
+ it("should parse tarball URLs", () => {
129
+ const args = ["https://example.com/local-package.tgz"];
130
+
131
+ const result = parsePackagesFromArguments(args);
132
+
133
+ assert.deepEqual(result, [
134
+ { name: "https://example.com/local-package.tgz", version: "latest" },
135
+ ]);
136
+ });
137
+
138
+ it("should parse git URLs", () => {
139
+ const args = ["git://github.com/http-party/http-server"];
140
+
141
+ const result = parsePackagesFromArguments(args);
142
+
143
+ assert.deepEqual(result, [
144
+ { name: "git://github.com/http-party/http-server", version: "latest" },
145
+ ]);
146
+ });
147
+ });
@@ -0,0 +1,17 @@
1
+ import { execSync } from "child_process";
2
+ import { ui } from "../../environment/userInteraction.js";
3
+
4
+ export function runNpx(args) {
5
+ try {
6
+ const npxCommand = `npx ${args.join(" ")}`;
7
+ execSync(npxCommand, { stdio: "inherit" });
8
+ } catch (error) {
9
+ if (error.status) {
10
+ return { status: error.status };
11
+ } else {
12
+ ui.writeError("Error executing command:", error.message);
13
+ return { status: 1 };
14
+ }
15
+ }
16
+ return { status: 0 };
17
+ }
@@ -0,0 +1,34 @@
1
+ import { commandArgumentScanner } from "./dependencyScanner/commandArgumentScanner.js";
2
+ import { runYarnCommand } from "./runYarnCommand.js";
3
+
4
+ const scanner = commandArgumentScanner();
5
+
6
+ export function createYarnPackageManager() {
7
+ return {
8
+ getWarningMessage: () => null,
9
+ runCommand: runYarnCommand,
10
+ isSupportedCommand: (args) =>
11
+ matchesCommand(args, "add") ||
12
+ matchesCommand(args, "global", "add") ||
13
+ matchesCommand(args, "install") ||
14
+ matchesCommand(args, "up") ||
15
+ matchesCommand(args, "upgrade") ||
16
+ matchesCommand(args, "global", "upgrade") ||
17
+ matchesCommand(args, "dlx"),
18
+ getDependencyUpdatesForCommand: (args) => scanner.scan(args),
19
+ };
20
+ }
21
+
22
+ function matchesCommand(args, ...commandArgs) {
23
+ if (args.length < commandArgs.length) {
24
+ return false;
25
+ }
26
+
27
+ for (var i = 0; i < commandArgs.length; i++) {
28
+ if (args[i].toLowerCase() !== commandArgs[i].toLowerCase()) {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
@@ -0,0 +1,28 @@
1
+ import { resolvePackageVersion } from "../../../api/npmApi.js";
2
+ import { parsePackagesFromArguments } from "../parsing/parsePackagesFromArguments.js";
3
+
4
+ export function commandArgumentScanner() {
5
+ return {
6
+ scan: (args) => scanDependencies(args),
7
+ shouldScan: () => true, // There's no dry run for yarn, so we always scan
8
+ };
9
+ }
10
+
11
+ async function scanDependencies(args) {
12
+ const changes = [];
13
+ const packageUpdates = parsePackagesFromArguments(args);
14
+
15
+ for (const packageUpdate of packageUpdates) {
16
+ var exactVersion = await resolvePackageVersion(
17
+ packageUpdate.name,
18
+ packageUpdate.version
19
+ );
20
+ if (exactVersion) {
21
+ packageUpdate.version = exactVersion;
22
+ }
23
+
24
+ changes.push({ ...packageUpdate, type: "add" });
25
+ }
26
+
27
+ return changes;
28
+ }
@@ -0,0 +1,102 @@
1
+ export function parsePackagesFromArguments(args) {
2
+ const changes = [];
3
+ let defaultTag = "latest";
4
+
5
+ for (let i = 1; i < args.length; i++) {
6
+ const arg = args[i];
7
+ const option = getOption(arg);
8
+
9
+ if (option) {
10
+ // If the option has a parameter, skip the next argument as well
11
+ i += option.numberOfParameters;
12
+
13
+ continue;
14
+ }
15
+
16
+ const packageDetails = parsePackagename(arg, defaultTag);
17
+ if (packageDetails) {
18
+ changes.push(packageDetails);
19
+ }
20
+ }
21
+
22
+ return changes;
23
+ }
24
+
25
+ function getOption(arg) {
26
+ if (isOptionWithParameter(arg)) {
27
+ return {
28
+ name: arg,
29
+ numberOfParameters: 1,
30
+ };
31
+ }
32
+
33
+ // Arguments starting with "-" or "--" are considered options
34
+ // except for "--package=" which contains the package name
35
+ if (arg.startsWith("-")) {
36
+ return {
37
+ name: arg,
38
+ numberOfParameters: 0,
39
+ };
40
+ }
41
+
42
+ return undefined;
43
+ }
44
+
45
+ function isOptionWithParameter(arg) {
46
+ const optionsWithParameters = [
47
+ "--use-yarnrc",
48
+ "--link-folder",
49
+ "--global-folder",
50
+ "--modules-folder",
51
+ "--preferred-cache-folder",
52
+ "--cache-folder",
53
+ "--mutex",
54
+ "--cwd",
55
+ "--proxy",
56
+ "--https-proxy",
57
+ "--registry",
58
+ "--network-concurrency",
59
+ "--network-timeout",
60
+ "--scripts-prepend-node-path",
61
+ "--otp",
62
+ ];
63
+
64
+ return optionsWithParameters.includes(arg);
65
+ }
66
+
67
+ function parsePackagename(arg, defaultTag) {
68
+ // format can be --package=name@version
69
+ // in that case, we need to remove the --package= part
70
+ if (arg.startsWith("--package=")) {
71
+ arg = arg.slice(10);
72
+ }
73
+
74
+ arg = removeAlias(arg);
75
+
76
+ // Split at the last "@" to separate the package name and version
77
+ const lastAtIndex = arg.lastIndexOf("@");
78
+
79
+ let name, version;
80
+ if (lastAtIndex !== -1) {
81
+ name = arg.slice(0, lastAtIndex);
82
+ version = arg.slice(lastAtIndex + 1);
83
+ } else {
84
+ name = arg;
85
+ version = defaultTag; // No tag specified (eg: "http-server"), use the default tag
86
+ }
87
+
88
+ return {
89
+ name,
90
+ version,
91
+ };
92
+ }
93
+
94
+ function removeAlias(arg) {
95
+ // removes the alias.
96
+ // Eg.: server@npm:http-server@latest becomes http-server@latest
97
+ const aliasIndex = arg.indexOf("@npm:");
98
+ if (aliasIndex !== -1) {
99
+ return arg.slice(aliasIndex + 5);
100
+ }
101
+ return arg;
102
+ }