mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Adapt per review
This commit is contained in:
parent
63b7a5ee5e
commit
6ff2ee3367
20 changed files with 118 additions and 119 deletions
|
|
@ -25,17 +25,17 @@ function Test-InstallDir {
|
|||
return @{ Ok = $false; Reason = "-InstallDir must not contain the PATH separator ($([System.IO.Path]::PathSeparator))" }
|
||||
}
|
||||
|
||||
$inputSegments = $Dir.Split([char[]]@('\', '/'), [System.StringSplitOptions]::RemoveEmptyEntries)
|
||||
if ($inputSegments -contains "..") {
|
||||
return @{ Ok = $false; Reason = "-InstallDir must not contain path traversal segments" }
|
||||
}
|
||||
|
||||
$normalized = [System.IO.Path]::GetFullPath($Dir)
|
||||
$root = [System.IO.Path]::GetPathRoot($normalized)
|
||||
if ($normalized.TrimEnd('\', '/') -eq $root.TrimEnd('\', '/')) {
|
||||
return @{ Ok = $false; Reason = "-InstallDir cannot be a root or drive-root directory" }
|
||||
}
|
||||
|
||||
$segments = $normalized.Substring($root.Length).Split([char[]]@('\', '/'), [System.StringSplitOptions]::RemoveEmptyEntries)
|
||||
if ($segments -contains "..") {
|
||||
return @{ Ok = $false; Reason = "-InstallDir must not contain path traversal segments" }
|
||||
}
|
||||
|
||||
return @{ Ok = $true; Normalized = $normalized }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import os from "os";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { getInstalledSafeChainDir } from "../installLocation.js";
|
||||
|
||||
/**
|
||||
|
|
@ -8,3 +9,49 @@ import { getInstalledSafeChainDir } from "../installLocation.js";
|
|||
export function getSafeChainBaseDir() {
|
||||
return getInstalledSafeChainDir() ?? path.join(os.homedir(), ".safe-chain");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getBinDir() {
|
||||
return path.join(getSafeChainBaseDir(), "bin");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getShimsDir() {
|
||||
return path.join(getSafeChainBaseDir(), "shims");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getScriptsDir() {
|
||||
return path.join(getSafeChainBaseDir(), "scripts");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getCertsDir() {
|
||||
return path.join(getSafeChainBaseDir(), "certs");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} moduleUrl
|
||||
* @param {string} fileName
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getStartupScriptSourcePath(moduleUrl, fileName) {
|
||||
return path.join(path.dirname(fileURLToPath(moduleUrl)), "startup-scripts", fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} moduleUrl
|
||||
* @param {string} fileName
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getPathWrapperTemplatePath(moduleUrl, fileName) {
|
||||
return path.join(path.dirname(fileURLToPath(moduleUrl)), "path-wrappers", "templates", fileName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
import forge from "node-forge";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { getSafeChainBaseDir } from "../config/safeChainDir.js";
|
||||
import { getCertsDir } from "../config/safeChainDir.js";
|
||||
|
||||
const ca = loadCa();
|
||||
|
||||
const certCache = new Map();
|
||||
|
||||
function getCertFolder() {
|
||||
return path.join(getSafeChainBaseDir(), "certs");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {forge.pki.PublicKey} publicKey
|
||||
* @returns {string}
|
||||
|
|
@ -23,7 +19,7 @@ function createKeyIdentifier(publicKey) {
|
|||
}
|
||||
|
||||
export function getCaCertPath() {
|
||||
return path.join(getCertFolder(), "ca-cert.pem");
|
||||
return path.join(getCertsDir(), "ca-cert.pem");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -115,7 +111,7 @@ export function generateCertForHost(hostname) {
|
|||
}
|
||||
|
||||
function loadCa() {
|
||||
const certFolder = getCertFolder();
|
||||
const certFolder = getCertsDir();
|
||||
const keyPath = path.join(certFolder, "ca-key.pem");
|
||||
const certPath = path.join(certFolder, "ca-cert.pem");
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ describe("certUtils", () => {
|
|||
mock.module("../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getSafeChainBaseDir: () => installedSafeChainDir ?? "/home/test/.safe-chain",
|
||||
getCertsDir: () => `${installedSafeChainDir ?? "/home/test/.safe-chain"}/certs`,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import * as os from "os";
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { ECOSYSTEM_JS, ECOSYSTEM_PY } from "../config/settings.js";
|
||||
import { getSafeChainBaseDir } from "../config/safeChainDir.js";
|
||||
import { safeSpawn } from "../utils/safeSpawn.js";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
|
||||
|
|
@ -122,34 +121,6 @@ export function getPackageManagerList() {
|
|||
return `${tools.join(", ")}, and ${lastTool} commands`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the safe-chain base directory.
|
||||
* Uses the packaged binary location when available, otherwise defaults to ~/.safe-chain.
|
||||
* @returns {string}
|
||||
*/
|
||||
export { getSafeChainBaseDir };
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getBinDir() {
|
||||
return path.join(getSafeChainBaseDir(), "bin");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getShimsDir() {
|
||||
return path.join(getSafeChainBaseDir(), "shims");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getScriptsDir() {
|
||||
return path.join(getSafeChainBaseDir(), "scripts");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} executableName
|
||||
*
|
||||
|
|
|
|||
|
|
@ -186,22 +186,27 @@ describe("removeLinesMatchingPatternTests", () => {
|
|||
|
||||
describe("getSafeChainBaseDir / getBinDir / getShimsDir / getScriptsDir", () => {
|
||||
it("defaults base dir to ~/.safe-chain when no packaged install dir is available", async () => {
|
||||
const { getSafeChainBaseDir } = await import("./helpers.js");
|
||||
const { getSafeChainBaseDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getSafeChainBaseDir(), path.join(homedir(), ".safe-chain"));
|
||||
});
|
||||
|
||||
it("getBinDir returns ~/.safe-chain/bin by default", async () => {
|
||||
const { getBinDir } = await import("./helpers.js");
|
||||
const { getBinDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getBinDir(), path.join(homedir(), ".safe-chain", "bin"));
|
||||
});
|
||||
|
||||
it("getShimsDir returns ~/.safe-chain/shims by default", async () => {
|
||||
const { getShimsDir } = await import("./helpers.js");
|
||||
const { getShimsDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getShimsDir(), path.join(homedir(), ".safe-chain", "shims"));
|
||||
});
|
||||
|
||||
it("getScriptsDir returns ~/.safe-chain/scripts by default", async () => {
|
||||
const { getScriptsDir } = await import("./helpers.js");
|
||||
const { getScriptsDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getScriptsDir(), path.join(homedir(), ".safe-chain", "scripts"));
|
||||
});
|
||||
|
||||
it("getCertsDir returns ~/.safe-chain/certs by default", async () => {
|
||||
const { getCertsDir } = await import("../config/safeChainDir.js");
|
||||
assert.strictEqual(getCertsDir(), path.join(homedir(), ".safe-chain", "certs"));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,24 +1,14 @@
|
|||
import chalk from "chalk";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
import { getPackageManagerList, knownAikidoTools, getShimsDir, getBinDir } from "./helpers.js";
|
||||
import { getPackageManagerList, knownAikidoTools } from "./helpers.js";
|
||||
import {
|
||||
getShimsDir,
|
||||
getBinDir,
|
||||
getPathWrapperTemplatePath,
|
||||
} from "../config/safeChainDir.js";
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
/** @type {string} */
|
||||
// This checks the current file's dirname in a way that's compatible with:
|
||||
// - Modulejs (import.meta.url)
|
||||
// - ES modules (__dirname)
|
||||
// This is needed because safe-chain's npm package is built using ES modules,
|
||||
// but building the binaries requires commonjs.
|
||||
let dirname;
|
||||
if (import.meta.url) {
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
dirname = path.dirname(filename);
|
||||
} else {
|
||||
dirname = __dirname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over the detected shells and calls the setup function for each.
|
||||
|
|
@ -50,12 +40,7 @@ export async function setupCi() {
|
|||
*/
|
||||
function createUnixShims(shimsDir) {
|
||||
// Read the template file
|
||||
const templatePath = path.resolve(
|
||||
dirname,
|
||||
"path-wrappers",
|
||||
"templates",
|
||||
"unix-wrapper.template.sh"
|
||||
);
|
||||
const templatePath = getPathWrapperTemplatePath(import.meta.url, "unix-wrapper.template.sh");
|
||||
|
||||
if (!fs.existsSync(templatePath)) {
|
||||
ui.writeError(`Template file not found: ${templatePath}`);
|
||||
|
|
@ -89,12 +74,7 @@ function createUnixShims(shimsDir) {
|
|||
*/
|
||||
function createWindowsShims(shimsDir) {
|
||||
// Read the template file
|
||||
const templatePath = path.resolve(
|
||||
dirname,
|
||||
"path-wrappers",
|
||||
"templates",
|
||||
"windows-wrapper.template.cmd"
|
||||
);
|
||||
const templatePath = getPathWrapperTemplatePath(import.meta.url, "windows-wrapper.template.cmd");
|
||||
|
||||
if (!fs.existsSync(templatePath)) {
|
||||
ui.writeError(`Windows template file not found: ${templatePath}`);
|
||||
|
|
|
|||
|
|
@ -50,8 +50,15 @@ describe("Setup CI shell integration", () => {
|
|||
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
||||
],
|
||||
getPackageManagerList: () => "npm, yarn",
|
||||
},
|
||||
});
|
||||
|
||||
mock.module("../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getShimsDir: () => mockShimsDir,
|
||||
getBinDir: () => path.join(mockHomeDir, ".safe-chain", "bin"),
|
||||
getPathWrapperTemplatePath: (_moduleUrl, fileName) =>
|
||||
path.join(mockTemplateDir, "path-wrappers", "templates", fileName),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -64,22 +71,6 @@ describe("Setup CI shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
// Mock path module to resolve templates correctly
|
||||
mock.module("path", {
|
||||
namedExports: {
|
||||
join: path.join,
|
||||
dirname: () => mockTemplateDir,
|
||||
resolve: (...args) => path.resolve(mockTemplateDir, ...args.slice(1)),
|
||||
},
|
||||
});
|
||||
|
||||
// Mock fileURLToPath
|
||||
mock.module("url", {
|
||||
namedExports: {
|
||||
fileURLToPath: () => path.join(mockTemplateDir, "setup-ci.js"),
|
||||
},
|
||||
});
|
||||
|
||||
// Import setupCi module after mocking
|
||||
setupCi = (await import("./setup-ci.js")).setupCi;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,28 +1,10 @@
|
|||
import chalk from "chalk";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
import { detectShells } from "./shellDetection.js";
|
||||
import {
|
||||
knownAikidoTools,
|
||||
getPackageManagerList,
|
||||
getScriptsDir,
|
||||
} from "./helpers.js";
|
||||
import { knownAikidoTools, getPackageManagerList } from "./helpers.js";
|
||||
import { getScriptsDir, getStartupScriptSourcePath } from "../config/safeChainDir.js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
/** @type {string} */
|
||||
// This checks the current file's dirname in a way that's compatible with:
|
||||
// - Modulejs (import.meta.url)
|
||||
// - ES modules (__dirname)
|
||||
// This is needed because safe-chain's npm package is built using ES modules,
|
||||
// but building the binaries requires commonjs.
|
||||
let dirname;
|
||||
if (import.meta.url) {
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
dirname = path.dirname(filename);
|
||||
} else {
|
||||
dirname = __dirname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over the detected shells and calls the setup function for each.
|
||||
|
|
@ -122,7 +104,7 @@ function copyStartupFiles() {
|
|||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
const sourcePath = path.join(dirname, "startup-scripts", file);
|
||||
const sourcePath = getStartupScriptSourcePath(import.meta.url, file);
|
||||
fs.copyFileSync(sourcePath, targetPath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import {
|
|||
addLineToFile,
|
||||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
getScriptsDir,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync, spawnSync } from "child_process";
|
||||
import * as os from "os";
|
||||
import path from "path";
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ describe("Bash shell integration", () => {
|
|||
mock.module("../helpers.js", {
|
||||
namedExports: {
|
||||
doesExecutableExistOnSystem: () => true,
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
addLineToFile: (filePath, line) => {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
fs.writeFileSync(filePath, "", "utf-8");
|
||||
|
|
@ -36,6 +35,12 @@ describe("Bash shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import {
|
|||
addLineToFile,
|
||||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
getScriptsDir,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ describe("Fish shell integration", () => {
|
|||
mock.module("../helpers.js", {
|
||||
namedExports: {
|
||||
doesExecutableExistOnSystem: () => true,
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
addLineToFile: (filePath, line) => {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
fs.writeFileSync(filePath, "", "utf-8");
|
||||
|
|
@ -34,6 +33,12 @@ describe("Fish shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import {
|
|||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
validatePowerShellExecutionPolicy,
|
||||
getScriptsDir,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ describe("PowerShell Core shell integration", () => {
|
|||
fs.writeFileSync(filePath, filteredLines.join("\n"), "utf-8");
|
||||
},
|
||||
validatePowerShellExecutionPolicy: () => executionPolicyResult,
|
||||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import {
|
|||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
validatePowerShellExecutionPolicy,
|
||||
getScriptsDir,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ describe("Windows PowerShell shell integration", () => {
|
|||
fs.writeFileSync(filePath, filteredLines.join("\n"), "utf-8");
|
||||
},
|
||||
validatePowerShellExecutionPolicy: () => executionPolicyResult,
|
||||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import {
|
|||
addLineToFile,
|
||||
doesExecutableExistOnSystem,
|
||||
removeLinesMatchingPattern,
|
||||
getScriptsDir,
|
||||
} from "../helpers.js";
|
||||
import { getScriptsDir } from "../../config/safeChainDir.js";
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ describe("Zsh shell integration", () => {
|
|||
mock.module("../helpers.js", {
|
||||
namedExports: {
|
||||
doesExecutableExistOnSystem: () => true,
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
addLineToFile: (filePath, line) => {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
fs.writeFileSync(filePath, "", "utf-8");
|
||||
|
|
@ -34,6 +33,12 @@ describe("Zsh shell integration", () => {
|
|||
},
|
||||
});
|
||||
|
||||
mock.module("../../config/safeChainDir.js", {
|
||||
namedExports: {
|
||||
getScriptsDir: () => "/test-home/.safe-chain/scripts",
|
||||
},
|
||||
});
|
||||
|
||||
// Mock child_process execSync
|
||||
mock.module("child_process", {
|
||||
namedExports: {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import chalk from "chalk";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
import { detectShells } from "./shellDetection.js";
|
||||
import { knownAikidoTools, getPackageManagerList, getShimsDir, getScriptsDir } from "./helpers.js";
|
||||
import { knownAikidoTools, getPackageManagerList } from "./helpers.js";
|
||||
import { getShimsDir, getScriptsDir } from "../config/safeChainDir.js";
|
||||
import fs from "fs";
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue