Uploads are locked hard on the demo, but an image pasted into rich text does not use multipart: it is saved as a data: URI inside the JSON body. refuseDataUri, mounted beside refuseMultipart, refuses any POST/PUT/PATCH/ DELETE whose body has a data: URI in any string, on any route, core's or a module's, with the usual 403 DEMO_LOCKED. Each string is first read the way a browser reads an attribute (HTML character references decoded, tabs and newlines removed), so data: and a data: split by a newline are refused too. "metadata:" and prose such as "Data: none, yet" pass. Reads are never checked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
87 lines
3.3 KiB
JavaScript
87 lines
3.3 KiB
JavaScript
// ── The demo lock, wired into the real app (DEMO_MODE.md §3.1) ─────────────
|
|
//
|
|
// demoLocks.test.js proves each rule against a stand-in app. This file proves
|
|
// the wiring: with DEMO_MODE=true, app.js mounts the lock ahead of every
|
|
// router, so a locked route is refused before its handler — before even its
|
|
// auth check — and an open one still reaches it. One process per flag value,
|
|
// since app.js decides at require time: this file is the demo, and every other
|
|
// test file that requires app.js is the normal deployment.
|
|
process.env.DB_HOST = '127.0.0.1'
|
|
process.env.DB_PORT = '59999'
|
|
process.env.DEMO_MODE = 'true'
|
|
process.env.MODULES_DIR = require('path').join(require('os').tmpdir(), 'rg-demo-app-no-modules')
|
|
|
|
const { test, after } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
|
|
const app = require('../src/app')
|
|
const db = require('../src/utils/db')
|
|
|
|
after(() => db.close())
|
|
|
|
async function withServer(fn) {
|
|
const server = await new Promise((resolve) => {
|
|
const s = app.listen(0, '127.0.0.1', () => resolve(s))
|
|
})
|
|
try {
|
|
return await fn(`http://127.0.0.1:${server.address().port}`)
|
|
} finally {
|
|
server.closeAllConnections()
|
|
await new Promise((resolve) => server.close(resolve))
|
|
}
|
|
}
|
|
|
|
test('a locked route answers DEMO_LOCKED before its own auth check', async () => {
|
|
await withServer(async (base) => {
|
|
const res = await fetch(`${base}/api/v1/admin/site-mode`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ mode: 'maintenance' }),
|
|
})
|
|
assert.equal(res.status, 403)
|
|
assert.equal((await res.json()).code, 'DEMO_LOCKED')
|
|
})
|
|
})
|
|
|
|
test('an open route still reaches its handler, which answers as usual', async () => {
|
|
await withServer(async (base) => {
|
|
const res = await fetch(`${base}/api/v1/admin/posts`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ title: 'x' }),
|
|
})
|
|
assert.equal(res.status, 401) // requireAuth, not the lock
|
|
})
|
|
})
|
|
|
|
test('an upload is refused before multer or auth sees it', async () => {
|
|
await withServer(async (base) => {
|
|
const form = new FormData()
|
|
form.append('image', new Blob(['x'], { type: 'image/png' }), 'x.png')
|
|
const res = await fetch(`${base}/api/v1/admin/uploads`, { method: 'POST', body: form })
|
|
assert.equal(res.status, 403)
|
|
assert.equal((await res.json()).code, 'DEMO_LOCKED')
|
|
})
|
|
})
|
|
|
|
test('a pasted data: image is refused before auth sees it, on an open route', async () => {
|
|
await withServer(async (base) => {
|
|
const res = await fetch(`${base}/api/v1/admin/posts`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ title: 'x', body: '<p><img src="data:image/png;base64,iVBORw0KGgo="></p>' }),
|
|
})
|
|
assert.equal(res.status, 403)
|
|
assert.equal((await res.json()).code, 'DEMO_LOCKED')
|
|
})
|
|
})
|
|
|
|
test('every response carries noindex, and robots.txt disallows everything', async () => {
|
|
await withServer(async (base) => {
|
|
const health = await fetch(`${base}/api/health`)
|
|
assert.equal(health.headers.get('x-robots-tag'), 'noindex, nofollow')
|
|
const robots = await fetch(`${base}/robots.txt`)
|
|
assert.equal(await robots.text(), 'User-agent: *\nDisallow: /\n')
|
|
})
|
|
})
|