security: harden gateway proxy, custom runtime proxy, and media routes (#95)

* security hardening pass 1 - otel removed

* hardening pass #2

* feat security hardening pass

* chore: trim unrelated docs from security hardening pr

* fix: address security hardening review findings

* address findings
This commit is contained in:
gsknnft
2026-04-03 18:02:06 -04:00
committed by GitHub
parent 083c146aac
commit 051d0ce469
14 changed files with 572 additions and 30 deletions
+30
View File
@@ -123,5 +123,35 @@ describe("/api/gateway/media route", () => {
expect(typeof options.maxBuffer).toBe("number");
expect(options.maxBuffer).toBeGreaterThan(payloadBytes.length);
});
it("rejects symlinked local media paths", async () => {
tempDir = makeTempDir("gateway-media-route-local-symlink");
const realHome = os.homedir();
const allowedRoot = path.join(realHome, ".openclaw");
const imagesDir = path.join(allowedRoot, "images");
const outsideDir = path.join(tempDir, "outside");
fs.mkdirSync(imagesDir, { recursive: true });
fs.mkdirSync(outsideDir, { recursive: true });
const outsideFile = path.join(outsideDir, "secret.png");
fs.writeFileSync(outsideFile, "not-allowed", "utf8");
const symlinkPath = path.join(imagesDir, "linked.png");
fs.symlinkSync(outsideFile, symlinkPath);
process.env.OPENCLAW_STATE_DIR = tempDir;
writeStudioSettings(tempDir, "ws://localhost:18789");
const response = await GET(
new Request(
`http://localhost/api/gateway/media?path=${encodeURIComponent(symlinkPath)}`
)
);
const body = (await response.json()) as { error?: string };
expect(response.status).toBe(400);
expect(body.error).toMatch(/symlink/i);
fs.rmSync(symlinkPath, { force: true });
});
});