Merge branch 'master' into test-quota

This commit is contained in:
Sam Rose 2025-04-10 16:19:09 +01:00 committed by GitHub
commit 551b3204fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 2 deletions

View File

@ -89,6 +89,8 @@ const CSP_DIRECTIVES = {
"worker-src": ["blob:", "'self'"],
}
const CSPDomainRegex = /^[A-Za-z0-9-*:/.]+$/
const contentSecurityPolicy = (async (ctx: Ctx, next: Next) => {
const nonce = crypto.randomBytes(16).toString("base64")
ctx.state.nonce = nonce
@ -108,8 +110,8 @@ const contentSecurityPolicy = (async (ctx: Ctx, next: Next) => {
if ("name" in appMetadata) {
for (let script of appMetadata.scripts || []) {
const inclusions = (script.cspWhitelist || "")
.split(",")
.filter(url => !!url?.trim().length)
.split("\n")
.filter(domain => CSPDomainRegex.test(domain))
directives["default-src"] = [
...directives["default-src"],
...inclusions,

View File

@ -132,4 +132,49 @@ describe("contentSecurityPolicy middleware", () => {
expect(app.getAppMetadata).toHaveBeenCalledWith(appId)
expect(next).toHaveBeenCalled()
})
it("should filter out invalid domains", async () => {
const appId = "app_foo"
const validDomain = "https://*.foo.bar"
const invalidDomain = "https:*&*(£$:\n;"
// Ctx setup to let us try and use CSP whitelist
ctx.appId = appId
ctx.user = users.user()
ctx.user.license = licenses.license({
features: [Feature.CUSTOM_APP_SCRIPTS],
})
// @ts-ignore
app.getAppMetadata.mockImplementation(function (): App {
return {
appId,
type: "foo",
version: "1",
componentLibraries: [],
name: "foo",
url: "/foo",
template: undefined,
instance: { _id: appId },
tenantId: ctx.user.tenantId,
status: "foo",
scripts: [
{
id: "foo",
name: "Test",
location: "Head",
cspWhitelist: validDomain + "\n" + invalidDomain,
},
],
}
})
await contentSecurityPolicy(ctx, next)
const cspHeader = ctx.set.mock.calls[0][1]
expect(cspHeader).toContain(`default-src 'self' ${validDomain};`)
expect(cspHeader).not.toContain(invalidDomain)
expect(app.getAppMetadata).toHaveBeenCalledWith(appId)
expect(next).toHaveBeenCalled()
})
})