Skip to content

Commit b37f609

Browse files
carderneTrigger.dev RepoOps
authored andcommitted
fix: harden self-hosting and repository tools
Mono-RevId: 6df559943fc3405b2933bc652111a55634090cad
1 parent 3a7d215 commit b37f609

3 files changed

Lines changed: 52 additions & 14 deletions

File tree

‎.github/workflows/docs.yml‎

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ on:
55
branches:
66
- main
77
paths:
8+
- ".github/workflows/docs.yml"
89
- "docs/**"
910
pull_request:
1011
types: [opened, synchronize, reopened]
1112
paths:
13+
- ".github/workflows/docs.yml"
1214
- "docs/**"
1315

1416
concurrency:
@@ -30,15 +32,22 @@ jobs:
3032
with:
3133
persist-credentials: false
3234

33-
- name: 📦 Cache npm
34-
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
35-
with:
36-
path: |
37-
~/.npm
38-
key: |
39-
${{ runner.os }}-mintlify
40-
restore-keys: |
41-
${{ runner.os }}-mintlify
35+
- name: 📦 Install Mintlify
36+
working-directory: ${{ runner.temp }}
37+
env:
38+
COREPACK_HOME: ${{ runner.temp }}/mintlify-corepack
39+
NPM_CONFIG_CACHE: ${{ runner.temp }}/mintlify-npm-cache
40+
NPM_CONFIG_GLOBALCONFIG: /dev/null
41+
NPM_CONFIG_USERCONFIG: /dev/null
42+
PNPM_HOME: ${{ runner.temp }}/mintlify-pnpm
43+
run: |
44+
mkdir -p "$RUNNER_TEMP/mintlify-tool"
45+
printf '{"private":true}\n' >"$RUNNER_TEMP/mintlify-tool/package.json"
46+
corepack pnpm@10.33.2 --dir "$RUNNER_TEMP/mintlify-tool" add \
47+
--save-exact \
48+
--store-dir "$RUNNER_TEMP/mintlify-pnpm-store" \
49+
--registry=https://registry.npmjs.org/ \
50+
mintlify@4.0.393
4251
4352
- name: 🔗 Check for broken links
44-
run: npx mintlify@4.0.393 broken-links
53+
run: '"$RUNNER_TEMP/mintlify-tool/node_modules/.bin/mintlify" broken-links'

‎hosting/docker/generate-secrets.sh‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# everyone out, or break an already-initialised datastore volume - so rotation is
77
# opt-in only, via --force (which WILL break existing data/sessions).
88
set -eu
9+
umask 077
910

1011
FORCE=0
1112
for arg in "$@"; do
@@ -35,6 +36,11 @@ if [ ! -f "$env_file" ]; then
3536
cp "$env_example" "$env_file"
3637
echo "Created $(basename "$env_file") from $(basename "$env_example")"
3738
fi
39+
chmod 600 "$env_file"
40+
41+
if [ -f "$htpasswd_file" ]; then
42+
chmod 600 "$htpasswd_file"
43+
fi
3844

3945
sed_inplace() {
4046
if [ "$(uname)" = "Darwin" ]; then
@@ -97,7 +103,9 @@ if [ "$FORCE" -eq 1 ] || [ -z "$(current_value DOCKER_REGISTRY_PASSWORD)" ]; the
97103
echo "ERROR: docker is required to hash the registry password (bcrypt). Install docker and re-run." >&2
98104
exit 1
99105
fi
100-
docker run --rm httpd:2 htpasswd -Bbn "$registry_user" "$registry_pass" >"$htpasswd_file"
106+
printf '%s\n' "$registry_pass" \
107+
| docker run --rm -i httpd:2 htpasswd -Bni "$registry_user" >"$htpasswd_file"
108+
chmod 600 "$htpasswd_file"
101109
set_var DOCKER_REGISTRY_PASSWORD "$registry_pass"
102110
echo "Generated DOCKER_REGISTRY_PASSWORD (and wrote $(basename "$htpasswd_file"))"
103111
generated=$((generated + 1))

‎scripts/unpack-worker.js‎

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,30 @@ try {
3232

3333
const id = data.id;
3434

35+
// The id becomes the extraction root, so it must be a single path component.
36+
if (
37+
typeof id !== "string" ||
38+
id.length === 0 ||
39+
id === "." ||
40+
id === ".." ||
41+
id.includes("/") ||
42+
id.includes("\\") ||
43+
id.includes("\0")
44+
) {
45+
throw new Error(`Unsafe worker id: ${id}`);
46+
}
47+
3548
console.log(`Extracting files for: ${id} to ${destDir}`);
3649

3750
destDir = path.join(destDir, id);
3851

3952
console.log(`Extracting files to: ${destDir}`);
4053

54+
// Resolve the extraction root once so we can keep every file inside it.
55+
const extractionRoot = path.resolve(destDir);
56+
4157
// Create the destination directory if it doesn't exist
42-
fs.mkdirSync(destDir, { recursive: true });
58+
fs.mkdirSync(extractionRoot, { recursive: true });
4359

4460
// Process each item in the array
4561
const sourceFiles = data.metadata.sourceFiles;
@@ -49,7 +65,12 @@ try {
4965
const decompressedContent = decompressContent(file.contents);
5066

5167
// Combine destination directory with file path
52-
const fullPath = path.join(destDir, file.filePath);
68+
const fullPath = path.resolve(extractionRoot, file.filePath);
69+
70+
// Reject any path that escapes the extraction root (zip-slip via ../ etc.)
71+
if (fullPath !== extractionRoot && !fullPath.startsWith(extractionRoot + path.sep)) {
72+
throw new Error(`Refusing to write outside destination: ${file.filePath}`);
73+
}
5374

5475
// Create directory structure if it doesn't exist
5576
const dirPath = path.dirname(fullPath);
@@ -61,7 +82,7 @@ try {
6182
console.log(`Created file: ${fullPath}`);
6283
});
6384

64-
console.log(`\nAll files have been extracted to: ${destDir}`);
85+
console.log(`\nAll files have been extracted to: ${extractionRoot}`);
6586
} catch (error) {
6687
console.error(error);
6788
process.exit(1);

0 commit comments

Comments
 (0)