@aikidosec/safe-chain 0.0.1-immutable-releases-beta

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 (112) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +517 -0
  3. package/bin/aikido-bun.js +14 -0
  4. package/bin/aikido-bunx.js +14 -0
  5. package/bin/aikido-npm.js +14 -0
  6. package/bin/aikido-npx.js +14 -0
  7. package/bin/aikido-pip.js +17 -0
  8. package/bin/aikido-pip3.js +17 -0
  9. package/bin/aikido-pipx.js +16 -0
  10. package/bin/aikido-pnpm.js +14 -0
  11. package/bin/aikido-pnpx.js +14 -0
  12. package/bin/aikido-poetry.js +13 -0
  13. package/bin/aikido-python.js +19 -0
  14. package/bin/aikido-python3.js +19 -0
  15. package/bin/aikido-uv.js +16 -0
  16. package/bin/aikido-yarn.js +14 -0
  17. package/bin/safe-chain.js +130 -0
  18. package/docs/banner.svg +151 -0
  19. package/docs/safe-package-manager-demo.gif +0 -0
  20. package/docs/safe-package-manager-demo.png +0 -0
  21. package/docs/shell-integration.md +149 -0
  22. package/docs/troubleshooting.md +321 -0
  23. package/npm-shrinkwrap.json +4069 -0
  24. package/package.json +72 -0
  25. package/src/api/aikido.js +187 -0
  26. package/src/api/npmApi.js +71 -0
  27. package/src/config/cliArguments.js +161 -0
  28. package/src/config/configFile.js +327 -0
  29. package/src/config/environmentVariables.js +57 -0
  30. package/src/config/settings.js +247 -0
  31. package/src/environment/environment.js +14 -0
  32. package/src/environment/userInteraction.js +122 -0
  33. package/src/main.js +123 -0
  34. package/src/packagemanager/_shared/commandErrors.js +17 -0
  35. package/src/packagemanager/_shared/matchesCommand.js +18 -0
  36. package/src/packagemanager/bun/createBunPackageManager.js +48 -0
  37. package/src/packagemanager/currentPackageManager.js +79 -0
  38. package/src/packagemanager/npm/createPackageManager.js +72 -0
  39. package/src/packagemanager/npm/dependencyScanner/commandArgumentScanner.js +74 -0
  40. package/src/packagemanager/npm/dependencyScanner/nullScanner.js +9 -0
  41. package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.js +144 -0
  42. package/src/packagemanager/npm/runNpmCommand.js +20 -0
  43. package/src/packagemanager/npm/utils/abbrevs-generated.js +359 -0
  44. package/src/packagemanager/npm/utils/cmd-list.js +174 -0
  45. package/src/packagemanager/npm/utils/npmCommands.js +34 -0
  46. package/src/packagemanager/npx/createPackageManager.js +15 -0
  47. package/src/packagemanager/npx/dependencyScanner/commandArgumentScanner.js +43 -0
  48. package/src/packagemanager/npx/parsing/parsePackagesFromArguments.js +130 -0
  49. package/src/packagemanager/npx/runNpxCommand.js +20 -0
  50. package/src/packagemanager/pip/createPackageManager.js +25 -0
  51. package/src/packagemanager/pip/pipSettings.js +6 -0
  52. package/src/packagemanager/pip/runPipCommand.js +209 -0
  53. package/src/packagemanager/pipx/createPipXPackageManager.js +18 -0
  54. package/src/packagemanager/pipx/runPipXCommand.js +60 -0
  55. package/src/packagemanager/pnpm/createPackageManager.js +57 -0
  56. package/src/packagemanager/pnpm/dependencyScanner/commandArgumentScanner.js +35 -0
  57. package/src/packagemanager/pnpm/parsing/parsePackagesFromArguments.js +109 -0
  58. package/src/packagemanager/pnpm/runPnpmCommand.js +32 -0
  59. package/src/packagemanager/poetry/createPoetryPackageManager.js +72 -0
  60. package/src/packagemanager/uv/createUvPackageManager.js +18 -0
  61. package/src/packagemanager/uv/runUvCommand.js +66 -0
  62. package/src/packagemanager/yarn/createPackageManager.js +41 -0
  63. package/src/packagemanager/yarn/dependencyScanner/commandArgumentScanner.js +35 -0
  64. package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.js +128 -0
  65. package/src/packagemanager/yarn/runYarnCommand.js +36 -0
  66. package/src/registryProxy/certBundle.js +203 -0
  67. package/src/registryProxy/certUtils.js +178 -0
  68. package/src/registryProxy/getConnectTimeout.js +13 -0
  69. package/src/registryProxy/http-utils.js +80 -0
  70. package/src/registryProxy/interceptors/createInterceptorForEcoSystem.js +25 -0
  71. package/src/registryProxy/interceptors/interceptorBuilder.js +179 -0
  72. package/src/registryProxy/interceptors/minimumPackageAgeExclusions.js +33 -0
  73. package/src/registryProxy/interceptors/npm/modifyNpmInfo.js +180 -0
  74. package/src/registryProxy/interceptors/npm/npmInterceptor.js +101 -0
  75. package/src/registryProxy/interceptors/npm/parseNpmPackageUrl.js +60 -0
  76. package/src/registryProxy/interceptors/pip/modifyPipInfo.js +167 -0
  77. package/src/registryProxy/interceptors/pip/modifyPipJsonResponse.js +176 -0
  78. package/src/registryProxy/interceptors/pip/parsePipPackageUrl.js +162 -0
  79. package/src/registryProxy/interceptors/pip/pipInterceptor.js +122 -0
  80. package/src/registryProxy/interceptors/pip/pipMetadataResponseUtils.js +27 -0
  81. package/src/registryProxy/interceptors/pip/pipMetadataVersionUtils.js +131 -0
  82. package/src/registryProxy/interceptors/suppressedVersionsState.js +21 -0
  83. package/src/registryProxy/isImdsEndpoint.js +13 -0
  84. package/src/registryProxy/mitmRequestHandler.js +240 -0
  85. package/src/registryProxy/plainHttpProxy.js +95 -0
  86. package/src/registryProxy/registryProxy.js +255 -0
  87. package/src/registryProxy/tunnelRequestHandler.js +213 -0
  88. package/src/scanning/audit/index.js +129 -0
  89. package/src/scanning/index.js +82 -0
  90. package/src/scanning/malwareDatabase.js +131 -0
  91. package/src/scanning/newPackagesDatabaseBuilder.js +71 -0
  92. package/src/scanning/newPackagesDatabaseWarnings.js +17 -0
  93. package/src/scanning/newPackagesListCache.js +126 -0
  94. package/src/scanning/packageNameVariants.js +29 -0
  95. package/src/shell-integration/helpers.js +304 -0
  96. package/src/shell-integration/path-wrappers/templates/unix-wrapper.template.sh +22 -0
  97. package/src/shell-integration/path-wrappers/templates/windows-wrapper.template.cmd +24 -0
  98. package/src/shell-integration/setup-ci.js +172 -0
  99. package/src/shell-integration/setup.js +129 -0
  100. package/src/shell-integration/shellDetection.js +39 -0
  101. package/src/shell-integration/startup-scripts/init-fish.fish +115 -0
  102. package/src/shell-integration/startup-scripts/init-posix.sh +96 -0
  103. package/src/shell-integration/startup-scripts/init-pwsh.ps1 +171 -0
  104. package/src/shell-integration/supported-shells/bash.js +152 -0
  105. package/src/shell-integration/supported-shells/fish.js +95 -0
  106. package/src/shell-integration/supported-shells/powershell.js +100 -0
  107. package/src/shell-integration/supported-shells/windowsPowershell.js +100 -0
  108. package/src/shell-integration/supported-shells/zsh.js +92 -0
  109. package/src/shell-integration/teardown.js +112 -0
  110. package/src/ultimate/ultimateTroubleshooting.js +111 -0
  111. package/src/utils/safeSpawn.js +153 -0
  112. package/tsconfig.json +21 -0
@@ -0,0 +1,95 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ } from "../helpers.js";
6
+ import { execSync } from "child_process";
7
+
8
+ const shellName = "Fish";
9
+ const executableName = "fish";
10
+ const startupFileCommand = "echo ~/.config/fish/config.fish";
11
+ const eol = "\n"; // When fish runs on Windows (e.g., Git Bash or WSL), it expects LF line endings.
12
+
13
+ function isInstalled() {
14
+ return doesExecutableExistOnSystem(executableName);
15
+ }
16
+
17
+ /**
18
+ * @param {import("../helpers.js").AikidoTool[]} tools
19
+ *
20
+ * @returns {boolean}
21
+ */
22
+ function teardown(tools) {
23
+ const startupFile = getStartupFile();
24
+
25
+ for (const { tool } of tools) {
26
+ // Remove any existing alias for the tool
27
+ removeLinesMatchingPattern(
28
+ startupFile,
29
+ new RegExp(`^alias\\s+${tool}\\s+`),
30
+ eol
31
+ );
32
+ }
33
+
34
+ // Removes the line that sources the safe-chain fish initialization script (~/.safe-chain/scripts/init-fish.fish)
35
+ removeLinesMatchingPattern(
36
+ startupFile,
37
+ /^source\s+~\/\.safe-chain\/scripts\/init-fish\.fish/,
38
+ eol
39
+ );
40
+
41
+ return true;
42
+ }
43
+
44
+ function setup() {
45
+ const startupFile = getStartupFile();
46
+
47
+ addLineToFile(
48
+ startupFile,
49
+ `source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script`,
50
+ eol
51
+ );
52
+
53
+ return true;
54
+ }
55
+
56
+ function getStartupFile() {
57
+ try {
58
+ return execSync(startupFileCommand, {
59
+ encoding: "utf8",
60
+ shell: executableName,
61
+ }).trim();
62
+ } catch (/** @type {any} */ error) {
63
+ throw new Error(
64
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`
65
+ );
66
+ }
67
+ }
68
+
69
+ function getManualTeardownInstructions() {
70
+ return [
71
+ `Remove the following line from your ~/.config/fish/config.fish file:`,
72
+ ` source ~/.safe-chain/scripts/init-fish.fish`,
73
+ `Then restart your terminal or run: source ~/.config/fish/config.fish`,
74
+ ];
75
+ }
76
+
77
+ function getManualSetupInstructions() {
78
+ return [
79
+ `Add the following line to your ~/.config/fish/config.fish file:`,
80
+ ` source ~/.safe-chain/scripts/init-fish.fish`,
81
+ `Then restart your terminal or run: source ~/.config/fish/config.fish`,
82
+ ];
83
+ }
84
+
85
+ /**
86
+ * @type {import("../shellDetection.js").Shell}
87
+ */
88
+ export default {
89
+ name: shellName,
90
+ isInstalled,
91
+ setup,
92
+ teardown,
93
+ getManualSetupInstructions,
94
+ getManualTeardownInstructions,
95
+ };
@@ -0,0 +1,100 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ validatePowerShellExecutionPolicy,
6
+ } from "../helpers.js";
7
+ import { execSync } from "child_process";
8
+
9
+ const shellName = "PowerShell Core";
10
+ const executableName = "pwsh";
11
+ const startupFileCommand = "echo $PROFILE";
12
+
13
+ function isInstalled() {
14
+ return doesExecutableExistOnSystem(executableName);
15
+ }
16
+
17
+ /**
18
+ * @param {import("../helpers.js").AikidoTool[]} tools
19
+ *
20
+ * @returns {boolean}
21
+ */
22
+ function teardown(tools) {
23
+ const startupFile = getStartupFile();
24
+
25
+ for (const { tool } of tools) {
26
+ // Remove any existing alias for the tool
27
+ removeLinesMatchingPattern(
28
+ startupFile,
29
+ new RegExp(`^Set-Alias\\s+${tool}\\s+`),
30
+ );
31
+ }
32
+
33
+ // Remove the line that sources the safe-chain PowerShell initialization script
34
+ removeLinesMatchingPattern(
35
+ startupFile,
36
+ /^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/,
37
+ );
38
+
39
+ return true;
40
+ }
41
+
42
+ async function setup() {
43
+ const { isValid, policy } =
44
+ await validatePowerShellExecutionPolicy(executableName);
45
+ if (!isValid) {
46
+ throw new Error(
47
+ `PowerShell execution policy is set to '${policy}', which prevents safe-chain from running.\n -> To fix this, open PowerShell as Administrator and run: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned.\n For more information, see: https://help.aikido.dev/code-scanning/aikido-malware-scanning/safe-chain-troubleshooting#powershell-execution-policy-blocks-scripts-windows`,
48
+ );
49
+ }
50
+
51
+ const startupFile = getStartupFile();
52
+
53
+ addLineToFile(
54
+ startupFile,
55
+ `. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`,
56
+ );
57
+
58
+ return true;
59
+ }
60
+
61
+ function getStartupFile() {
62
+ try {
63
+ return execSync(startupFileCommand, {
64
+ encoding: "utf8",
65
+ shell: executableName,
66
+ }).trim();
67
+ } catch (/** @type {any} */ error) {
68
+ throw new Error(
69
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`,
70
+ );
71
+ }
72
+ }
73
+
74
+ function getManualTeardownInstructions() {
75
+ return [
76
+ `Remove the following line from your PowerShell profile (run "echo $PROFILE" to find its location):`,
77
+ ` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
78
+ `Then restart your terminal or run: . $PROFILE`,
79
+ ];
80
+ }
81
+
82
+ function getManualSetupInstructions() {
83
+ return [
84
+ `Add the following line to your PowerShell profile (run "echo $PROFILE" to find its location):`,
85
+ ` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
86
+ `Then restart your terminal or run: . $PROFILE`,
87
+ ];
88
+ }
89
+
90
+ /**
91
+ * @type {import("../shellDetection.js").Shell}
92
+ */
93
+ export default {
94
+ name: shellName,
95
+ isInstalled,
96
+ setup,
97
+ teardown,
98
+ getManualSetupInstructions,
99
+ getManualTeardownInstructions,
100
+ };
@@ -0,0 +1,100 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ validatePowerShellExecutionPolicy,
6
+ } from "../helpers.js";
7
+ import { execSync } from "child_process";
8
+
9
+ const shellName = "Windows PowerShell";
10
+ const executableName = "powershell";
11
+ const startupFileCommand = "echo $PROFILE";
12
+
13
+ function isInstalled() {
14
+ return doesExecutableExistOnSystem(executableName);
15
+ }
16
+
17
+ /**
18
+ * @param {import("../helpers.js").AikidoTool[]} tools
19
+ *
20
+ * @returns {boolean}
21
+ */
22
+ function teardown(tools) {
23
+ const startupFile = getStartupFile();
24
+
25
+ for (const { tool } of tools) {
26
+ // Remove any existing alias for the tool
27
+ removeLinesMatchingPattern(
28
+ startupFile,
29
+ new RegExp(`^Set-Alias\\s+${tool}\\s+`),
30
+ );
31
+ }
32
+
33
+ // Remove the line that sources the safe-chain PowerShell initialization script
34
+ removeLinesMatchingPattern(
35
+ startupFile,
36
+ /^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/,
37
+ );
38
+
39
+ return true;
40
+ }
41
+
42
+ async function setup() {
43
+ const { isValid, policy } =
44
+ await validatePowerShellExecutionPolicy(executableName);
45
+ if (!isValid) {
46
+ throw new Error(
47
+ `PowerShell execution policy is set to '${policy}', which prevents safe-chain from running.\n -> To fix this, open PowerShell as Administrator and run: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned.\n For more information, see: https://help.aikido.dev/code-scanning/aikido-malware-scanning/safe-chain-troubleshooting#powershell-execution-policy-blocks-scripts-windows`,
48
+ );
49
+ }
50
+
51
+ const startupFile = getStartupFile();
52
+
53
+ addLineToFile(
54
+ startupFile,
55
+ `. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script`,
56
+ );
57
+
58
+ return true;
59
+ }
60
+
61
+ function getStartupFile() {
62
+ try {
63
+ return execSync(startupFileCommand, {
64
+ encoding: "utf8",
65
+ shell: executableName,
66
+ }).trim();
67
+ } catch (/** @type {any} */ error) {
68
+ throw new Error(
69
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`,
70
+ );
71
+ }
72
+ }
73
+
74
+ function getManualTeardownInstructions() {
75
+ return [
76
+ `Remove the following line from your PowerShell profile (run "echo $PROFILE" to find its location):`,
77
+ ` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
78
+ `Then restart your terminal or run: . $PROFILE`,
79
+ ];
80
+ }
81
+
82
+ function getManualSetupInstructions() {
83
+ return [
84
+ `Add the following line to your PowerShell profile (run "echo $PROFILE" to find its location):`,
85
+ ` . "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"`,
86
+ `Then restart your terminal or run: . $PROFILE`,
87
+ ];
88
+ }
89
+
90
+ /**
91
+ * @type {import("../shellDetection.js").Shell}
92
+ */
93
+ export default {
94
+ name: shellName,
95
+ isInstalled,
96
+ setup,
97
+ teardown,
98
+ getManualSetupInstructions,
99
+ getManualTeardownInstructions,
100
+ };
@@ -0,0 +1,92 @@
1
+ import {
2
+ addLineToFile,
3
+ doesExecutableExistOnSystem,
4
+ removeLinesMatchingPattern,
5
+ } from "../helpers.js";
6
+ import { execSync } from "child_process";
7
+
8
+ const shellName = "Zsh";
9
+ const executableName = "zsh";
10
+ const startupFileCommand = "echo ${ZDOTDIR:-$HOME}/.zshrc";
11
+ const eol = "\n"; // When zsh runs on Windows (e.g., Git Bash or WSL), it expects LF line endings.
12
+
13
+ function isInstalled() {
14
+ return doesExecutableExistOnSystem(executableName);
15
+ }
16
+
17
+ /**
18
+ * @param {import("../helpers.js").AikidoTool[]} tools
19
+ *
20
+ * @returns {boolean}
21
+ */
22
+ function teardown(tools) {
23
+ const startupFile = getStartupFile();
24
+
25
+ for (const { tool } of tools) {
26
+ // Remove any existing alias for the tool
27
+ removeLinesMatchingPattern(
28
+ startupFile,
29
+ new RegExp(`^alias\\s+${tool}=`),
30
+ eol
31
+ );
32
+ }
33
+
34
+ // Removes the line that sources the safe-chain zsh initialization script (~/.safe-chain/scripts/init-posix.sh)
35
+ removeLinesMatchingPattern(
36
+ startupFile,
37
+ /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/,
38
+ eol
39
+ );
40
+
41
+ return true;
42
+ }
43
+
44
+ function setup() {
45
+ const startupFile = getStartupFile();
46
+
47
+ addLineToFile(
48
+ startupFile,
49
+ `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script`,
50
+ eol
51
+ );
52
+
53
+ return true;
54
+ }
55
+
56
+ function getStartupFile() {
57
+ try {
58
+ return execSync(startupFileCommand, {
59
+ encoding: "utf8",
60
+ shell: executableName,
61
+ }).trim();
62
+ } catch (/** @type {any} */ error) {
63
+ throw new Error(
64
+ `Command failed: ${startupFileCommand}. Error: ${error.message}`
65
+ );
66
+ }
67
+ }
68
+
69
+ function getManualTeardownInstructions() {
70
+ return [
71
+ `Remove the following line from your ~/.zshrc file:`,
72
+ ` source ~/.safe-chain/scripts/init-posix.sh`,
73
+ `Then restart your terminal or run: source ~/.zshrc`,
74
+ ];
75
+ }
76
+
77
+ function getManualSetupInstructions() {
78
+ return [
79
+ `Add the following line to your ~/.zshrc file:`,
80
+ ` source ~/.safe-chain/scripts/init-posix.sh`,
81
+ `Then restart your terminal or run: source ~/.zshrc`,
82
+ ];
83
+ }
84
+
85
+ export default {
86
+ name: shellName,
87
+ isInstalled,
88
+ setup,
89
+ teardown,
90
+ getManualSetupInstructions,
91
+ getManualTeardownInstructions,
92
+ };
@@ -0,0 +1,112 @@
1
+ import chalk from "chalk";
2
+ import { ui } from "../environment/userInteraction.js";
3
+ import { detectShells } from "./shellDetection.js";
4
+ import { knownAikidoTools, getPackageManagerList, getShimsDir, getScriptsDir } from "./helpers.js";
5
+ import fs from "fs";
6
+
7
+ /**
8
+ * @returns {Promise<void>}
9
+ */
10
+ export async function teardown() {
11
+ ui.writeInformation(
12
+ chalk.bold("Removing shell aliases.") +
13
+ ` This will remove safe-chain aliases for ${getPackageManagerList()}.`
14
+ );
15
+ ui.emptyLine();
16
+
17
+ try {
18
+ const shells = detectShells();
19
+ if (shells.length === 0) {
20
+ ui.writeError("No supported shells detected. Cannot remove aliases.");
21
+ return;
22
+ }
23
+
24
+ ui.writeInformation(
25
+ `Detected ${shells.length} supported shell(s): ${shells
26
+ .map((shell) => chalk.bold(shell.name))
27
+ .join(", ")}.`
28
+ );
29
+
30
+ let updatedCount = 0;
31
+ for (const shell of shells) {
32
+ let success = false;
33
+ try {
34
+ success = shell.teardown(knownAikidoTools);
35
+ } catch {
36
+ success = false;
37
+ }
38
+
39
+ if (success) {
40
+ ui.writeInformation(
41
+ `${chalk.bold("- " + shell.name + ":")} ${chalk.green(
42
+ "Teardown successful"
43
+ )}`
44
+ );
45
+ updatedCount++;
46
+ } else {
47
+ ui.writeError(
48
+ `${chalk.bold("- " + shell.name + ":")} ${chalk.red(
49
+ "Teardown failed"
50
+ )}`
51
+ );
52
+ ui.emptyLine();
53
+ ui.writeInformation(` ${chalk.bold("To tear down manually:")}`);
54
+ for (const instruction of shell.getManualTeardownInstructions()) {
55
+ ui.writeInformation(` ${instruction}`);
56
+ }
57
+ ui.emptyLine();
58
+ }
59
+ }
60
+
61
+ if (updatedCount > 0) {
62
+ ui.emptyLine();
63
+ ui.writeInformation(`Please restart your terminal to apply the changes.`);
64
+ }
65
+ } catch (/** @type {any} */ error) {
66
+ ui.writeError(
67
+ `Failed to remove shell aliases: ${error.message}. Please check your shell configuration.`
68
+ );
69
+ return;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Removes directories created by setup-ci and setup commands
75
+ * @returns {Promise<void>}
76
+ */
77
+ export async function teardownDirectories() {
78
+ const shimsDir = getShimsDir();
79
+ const scriptsDir = getScriptsDir();
80
+
81
+ // Remove CI shims directory
82
+ if (fs.existsSync(shimsDir)) {
83
+ try {
84
+ fs.rmSync(shimsDir, { recursive: true, force: true });
85
+ ui.writeInformation(
86
+ `${chalk.bold("- CI Shims:")} ${chalk.green("Removed successfully")}`
87
+ );
88
+ } catch (/** @type {any} */ error) {
89
+ ui.writeError(
90
+ `${chalk.bold("- CI Shims:")} ${chalk.red(
91
+ "Failed to remove"
92
+ )}. Error: ${error.message}`
93
+ );
94
+ }
95
+ }
96
+
97
+ // Remove scripts directory
98
+ if (fs.existsSync(scriptsDir)) {
99
+ try {
100
+ fs.rmSync(scriptsDir, { recursive: true, force: true });
101
+ ui.writeInformation(
102
+ `${chalk.bold("- Scripts:")} ${chalk.green("Removed successfully")}`
103
+ );
104
+ } catch (/** @type {any} */ error) {
105
+ ui.writeError(
106
+ `${chalk.bold("- Scripts:")} ${chalk.red(
107
+ "Failed to remove"
108
+ )}. Error: ${error.message}`
109
+ );
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,111 @@
1
+ import { platform } from 'os';
2
+ import { ui } from "../environment/userInteraction.js";
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import {randomUUID} from "node:crypto";
5
+ import {createWriteStream} from "fs";
6
+ import archiver from 'archiver';
7
+ import path from "node:path";
8
+
9
+ export async function printUltimateLogs() {
10
+ const { proxyLogPath, ultimateLogPath, proxyErrLogPath, ultimateErrLogPath } = getPathsPerPlatform();
11
+
12
+ await printLogs(
13
+ "SafeChain Proxy",
14
+ proxyLogPath,
15
+ proxyErrLogPath
16
+ );
17
+
18
+ await printLogs(
19
+ "SafeChain Ultimate",
20
+ ultimateLogPath,
21
+ ultimateErrLogPath
22
+ );
23
+ }
24
+
25
+ export async function troubleshootingExport() {
26
+ const { logDir } = getPathsPerPlatform();
27
+ return new Promise((resolve, reject) => {
28
+ if (!existsSync(logDir)) {
29
+ ui.writeError(`Log directory not found: ${logDir}`);
30
+ reject(new Error(`Log directory not found: ${logDir}`));
31
+ return;
32
+ }
33
+
34
+ const date = new Date().toISOString().split('T')[0];
35
+ const uuid = randomUUID();
36
+ const zipFileName = `safechain-ultimate-${date}-${uuid}.zip`;
37
+ const output = createWriteStream(zipFileName);
38
+ const archive = archiver('zip', { zlib: { level: 9 } });
39
+
40
+ output.on('close', () => {
41
+ ui.writeInformation(`Logs collected and zipped as: ${path.resolve(zipFileName)}`);
42
+ resolve(zipFileName);
43
+ });
44
+
45
+ archive.on('error', (/** @type {Error} */ err) => {
46
+ ui.writeError(`Failed to zip logs: ${err.message}`);
47
+ reject(err);
48
+ });
49
+
50
+ archive.pipe(output);
51
+ archive.directory(logDir, false);
52
+ archive.finalize();
53
+ });
54
+ }
55
+
56
+
57
+ function getPathsPerPlatform() {
58
+ const os = platform();
59
+ if (os === 'win32') {
60
+ const logDir = `C:\\ProgramData\\AikidoSecurity\\SafeChainUltimate\\logs`;
61
+ return {
62
+ logDir,
63
+ proxyLogPath: `${logDir}\\SafeChainProxy.log`,
64
+ ultimateLogPath: `${logDir}\\SafeChainUltimate.log`,
65
+ proxyErrLogPath: `${logDir}\\SafeChainProxy.err`,
66
+ ultimateErrLogPath: `${logDir}\\SafeChainUltimate.err`,
67
+ };
68
+ } else if (os === 'darwin') {
69
+ const logDir = `/Library/Logs/AikidoSecurity/SafeChainUltimate`;
70
+ return {
71
+ logDir,
72
+ proxyLogPath: `${logDir}/safechain-proxy.log`,
73
+ ultimateLogPath: `${logDir}/safechain-ultimate.log`,
74
+ proxyErrLogPath: `${logDir}/safechain-proxy.error.log`,
75
+ ultimateErrLogPath: `${logDir}/safechain-ultimate.error.log`,
76
+ };
77
+ } else {
78
+ throw new Error('Unsupported platform for log printing.');
79
+ }
80
+ }
81
+
82
+ /**
83
+ * @param {string} appName
84
+ * @param {string} logPath
85
+ * @param {string} errLogPath
86
+ */
87
+ async function printLogs(appName, logPath, errLogPath) {
88
+ ui.writeInformation(`=== ${appName} Logs ===`);
89
+ try {
90
+ if (existsSync(logPath)) {
91
+ const logs = readFileSync(logPath, "utf-8");
92
+ ui.writeInformation(logs);
93
+ } else {
94
+ ui.writeWarning(`${appName} log file not found: ${logPath}`);
95
+ }
96
+ } catch (error) {
97
+ ui.writeError(`Failed to read ${appName} logs: ${error}`);
98
+ }
99
+
100
+ ui.writeInformation(`=== ${appName} Error Logs ===`);
101
+ try {
102
+ if (existsSync(errLogPath)) {
103
+ const errLogs = readFileSync(errLogPath, "utf-8");
104
+ ui.writeInformation(errLogs);
105
+ } else {
106
+ ui.writeInformation(`No error log file found for ${appName}.`);
107
+ }
108
+ } catch (error) {
109
+ ui.writeError(`Failed to read ${appName} error logs: ${error}`);
110
+ }
111
+ }