chore: remove temporary issue 416 security workflow

This commit is contained in:
Furox 2026-09-05 02:09:18 +03:00 committed by Furox88
parent f6ac70d7d5
commit 9fa33160cf

View File

@ -1,150 +0,0 @@
name: Issue 416 Security Round 5
on:
push:
branches:
- feat/issue-416-backup-destinations
paths:
- .github/workflows/issue-416-security-round5.yml
permissions:
contents: write
jobs:
fix-and-verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
ref: feat/issue-416-backup-destinations
- uses: pnpm/action-setup@v5
with:
version: 10.22.0
- uses: actions/setup-node@v5
with:
node-version: 24.4.0
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Apply guarded final hardening
run: |
python - <<'PY'
from pathlib import Path
def replace_once(text, old, new, label):
count = text.count(old)
if count != 1:
raise SystemExit(f"{label}: expected one match, found {count}")
return text.replace(old, new, 1)
utils = Path("packages/server/src/utils/backups/utils.ts")
text = utils.read_text()
old = '''\t\tif (destination.secretAccessKey) {\n\t\t\tconst obscuredPassword = await obscureRclonePassword(\n\t\t\t\tdestination.secretAccessKey,\n\t\t\t);\n\t\t\tflags.push(`--${backend}-pass=${quote([obscuredPassword])}`);\n\t\t}\n\t\tflags.push(...additionalFlags);\n\t\treturn {\n'''
new = '''\t\tif (destination.secretAccessKey) {\n\t\t\tconst obscuredPassword = await obscureRclonePassword(\n\t\t\t\tdestination.secretAccessKey,\n\t\t\t);\n\t\t\tflags.push(`--${backend}-pass=${quote([obscuredPassword])}`);\n\t\t}\n\t\tflags.push(...additionalFlags);\n\t\tif (provider === RCLONE_DESTINATION_PROVIDERS.FTP) {\n\t\t\t// CLI options override RCLONE_* environment defaults. Keep TLS\n\t\t\t// certificate verification enabled on the execution host.\n\t\t\tflags.push(\n\t\t\t\t"--ftp-no-check-certificate=false",\n\t\t\t\t"--no-check-certificate=false",\n\t\t\t);\n\t\t}\n\t\treturn {\n'''
text = replace_once(text, old, new, "FTP secure CLI overrides")
utils.write_text(text)
redact = Path("packages/server/src/utils/backups/redact.ts")
existing = redact.read_text()
required = [
"export const redactRcloneCredentials",
"sftp-key-file-pass",
"export const getSafeRcloneErrorMessage",
]
if not all(marker in existing for marker in required):
raise SystemExit("redact.ts no longer matches audited structure")
redact.write_text(r'''/**
* Redacts credentials from rclone command strings before they reach logs or
* user-facing error output. Handles both the existing S3 flags and the
* provider-specific FTP/SFTP credential flags used by backup destinations.
*/
export const redactRcloneCredentials = (command: string): string => {
return command.replace(
/(--(?:s3-access-key-id|s3-secret-access-key|ftp-pass|sftp-pass|sftp-key-file-pass)=)(?:(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\\[^\r\n]|[^\s"'\\])+)/g,
'$1"[REDACTED]"',
);
};
export const getSafeRcloneErrorMessage = (error: unknown): string =>
redactRcloneCredentials(
error instanceof Error ? error.message : String(error),
);
''')
test = Path("apps/dokploy/__test__/backups/redact-credentials.test.ts")
text = test.read_text()
if 'from "shell-quote"' not in text:
text = replace_once(
text,
'import { describe, expect, it } from "vitest";',
'import { quote } from "shell-quote";\nimport { describe, expect, it } from "vitest";',
"shell-quote test import",
)
extra = '''\n\tit("should fully redact shell-quote output with embedded quotes and whitespace", () => {\n\t\tconst secret = "PART_A' PART_B\\\" $PART_C;\\\\PART_D";\n\t\tconst cmd = `rclone lsf --s3-secret-access-key=${quote([secret])} --s3-region=us-east-1 :s3:bucket`;\n\t\tconst redacted = redactRcloneCredentials(cmd);\n\n\t\tfor (const fragment of ["PART_A", "PART_B", "PART_C", "PART_D"]) {\n\t\t\texpect(redacted).not.toContain(fragment);\n\t\t}\n\t\texpect(redacted).toContain('--s3-secret-access-key="[REDACTED]"');\n\t\texpect(redacted).toContain("--s3-region=us-east-1");\n\t});\n'''
idx = text.rfind("\n});")
if idx == -1:
raise SystemExit("redaction test describe end not found")
if "fully redact shell-quote output" not in text:
text = text[:idx] + extra + text[idx:]
test.write_text(text)
backup_test = Path("apps/dokploy/__test__/utils/backups.test.ts")
text = backup_test.read_text()
extra = '''\n\ttest("forces certificate verification on after user flags", async () => {\n\t\tconst result = await getRclonePathAndFlags(\n\t\t\tdestination({\n\t\t\t\tprovider: RCLONE_DESTINATION_PROVIDERS.FTP,\n\t\t\t\tendpoint: "storage.example.com",\n\t\t\t\taccessKey: "backup-user",\n\t\t\t\tsecretAccessKey: "",\n\t\t\t\tregion: "",\n\t\t\t\tbucket: "backups",\n\t\t\t\tadditionalFlags: ["--ftp-explicit-tls"],\n\t\t\t}),\n\t\t);\n\t\texpect(result.flags.slice(-2)).toEqual([\n\t\t\t"--ftp-no-check-certificate=false",\n\t\t\t"--no-check-certificate=false",\n\t\t]);\n\t});\n'''
idx = text.rfind("\n});")
if idx == -1:
raise SystemExit("FTP test describe end not found")
if "forces certificate verification on after user flags" not in text:
text = text[:idx] + extra + text[idx:]
backup_test.write_text(text)
PY
- name: Format changed files
run: |
pnpm exec biome check --write \
packages/server/src/utils/backups/utils.ts \
packages/server/src/utils/backups/redact.ts \
apps/dokploy/__test__/backups/redact-credentials.test.ts \
apps/dokploy/__test__/utils/backups.test.ts
- name: Focused backup and security regressions
run: |
pnpm --filter=dokploy exec vitest --config __test__/vitest.config.ts \
__test__/backups/redact-credentials.test.ts \
__test__/utils/backups.test.ts \
__test__/utils/issue-416-path-safety.test.ts --run
- name: Typecheck
run: pnpm typecheck
- name: Server build
run: pnpm server:build
- name: Biome clean
run: |
pnpm exec biome check \
packages/server/src/utils/backups/utils.ts \
packages/server/src/utils/backups/redact.ts \
apps/dokploy/__test__/backups/redact-credentials.test.ts \
apps/dokploy/__test__/utils/backups.test.ts
- name: Verify final security invariants
run: |
python - <<'PY'
from pathlib import Path
u=Path("packages/server/src/utils/backups/utils.ts").read_text()
assert '"--ftp-no-check-certificate=false"' in u
assert '"--no-check-certificate=false"' in u
r=Path("packages/server/src/utils/backups/redact.ts").read_text()
assert "sftp-key-file-pass" in r
assert "[^\\r\\n]" in r
t=Path("apps/dokploy/__test__/backups/redact-credentials.test.ts").read_text()
assert 'fully redact shell-quote output' in t
print("environment-default and complete shell-word redaction invariants verified")
PY
- name: Commit verified hardening
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add \
packages/server/src/utils/backups/utils.ts \
packages/server/src/utils/backups/redact.ts \
apps/dokploy/__test__/backups/redact-credentials.test.ts \
apps/dokploy/__test__/utils/backups.test.ts
if ! git diff --cached --quiet; then
git commit -m "fix: harden rclone environment and credential redaction"
git push origin HEAD:feat/issue-416-backup-destinations
fi