hardening

This commit is contained in:
Gregor Lohaus
2026-05-22 08:55:28 +02:00
parent ca4a02ab4e
commit 3110eefcbd
8 changed files with 293 additions and 173 deletions

View File

@@ -1,8 +1,8 @@
import { expect, test, beforeEach, afterEach } from 'bun:test'
import { initRenderer, SchemaMismatchError } from ".";
import { initRenderer, SchemaMismatchError } from "./index";
import { z } from "zod"
import { mkdtempSync, rmSync, existsSync, readFileSync } from "node:fs"
import { join } from "node:path"
import { mkdtempSync, rmSync, existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"
import { basename, join } from "node:path"
import { tmpdir } from "node:os"
const ifExampleSchema = z.object({
@@ -245,13 +245,78 @@ test("file if",() => {
})
test("no endif should throw",() => {
const createRenderer = initRenderer("./testdata/no_end_if")
expect(() => initRenderer("./testdata/no_end_if")).toThrow()
})
test("path vars cannot write outside target",() => {
const createRenderer = initRenderer("./testdata/var_in_path")
const sentinel = join(tmp, "keep.txt")
const outsideName = `${basename(tmp)}-outside`
const outsidePath = join(tmp, "..", outsideName)
writeFileSync(sentinel, "keep")
const render = createRenderer(z.object({
test: z.boolean(),
web: z.object({
create: z.boolean(),
dir: z.string()
}),
header: z.object({
render: z.boolean(),
text: z.string()
})
}))
expect( () => render(tmp,{
test: true,
expect(() => render(tmp,{
web: {
create: true,
dir: `../${outsideName}`
},
header: {
render: false,
text: "test"
}
})).toThrow()
expect(existsSync(outsidePath)).toBe(false)
expect(existsSync(sentinel)).toBe(true)
})
test("refuses overlapping source and target directories",() => {
const source = join(tmp, "template")
const target = join(source, "out")
mkdirSync(source, { recursive: true })
writeFileSync(join(source, "test.txt"), "test")
const createRenderer = initRenderer(source)
const render = createRenderer(z.object({}))
expect(() => render(target, {})).toThrow()
})
test("binary files are copied without text rendering",() => {
const source = join(tmp, "template")
const target = join(tmp, "out")
mkdirSync(source, { recursive: true })
const binary = Buffer.from([0, 255, 1, 2, 3])
writeFileSync(join(source, "asset.bin"), binary)
const createRenderer = initRenderer(source)
const render = createRenderer(z.object({}))
render(target, {})
expect(readFileSync(join(target, "asset.bin"))).toEqual(binary)
})
test("conflicting template variable types should throw",() => {
const source = join(tmp, "template")
mkdirSync(source, { recursive: true })
writeFileSync(join(source, "test.txt"), [
"<@if(context.test)>",
"<@endif>",
"<@var(context.test)>"
].join("\n"))
const createRenderer = initRenderer(source)
expect(() => createRenderer(z.object({
test: z.boolean(),
}))).toThrow()
})
test("if elseif else",() => {