Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/tall-needles-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'astro': major
---

Makes `Astro.csp` and `context.csp` optional instead of throwing if CSP is not enabled
Comment thread
florian-lefebvre marked this conversation as resolved.
Outdated

Until now, `context.csp` was always defined but would throw if CSP was not enabled in the Astro config. `context.csp` can now be undefined if CSP is not enabled and its methods will never throw.
Comment thread
florian-lefebvre marked this conversation as resolved.
Outdated

#### What should I do?

If you are using CSP runtime utilities, access methods conditionally:
Comment thread
florian-lefebvre marked this conversation as resolved.
Outdated

```diff
-Astro.csp.insertDirective("default-src 'self'");
+Astro.csp?.insertDirective("default-src 'self'");
```
13 changes: 0 additions & 13 deletions packages/astro/src/core/errors/errors-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1413,19 +1413,6 @@ export const FontFamilyNotFound = {
hint: 'This is often caused by a typo. Check that the `<Font />` component or `getFontData()` function are using a `cssVariable` specified in your config.',
} satisfies ErrorData;

/**
* @docs
* @description
* The CSP feature isn't enabled
* @message
* The `security.csp` configuration isn't enabled.
*/
export const CspNotEnabled = {
name: 'CspNotEnabled',
title: "CSP feature isn't enabled",
message: "The `security.csp` configuration isn't enabled.",
} satisfies ErrorData;

/**
* @docs
* @description
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/core/logger/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type LoggerLabel =
| 'update'
| 'adapter'
| 'islands'
| 'csp'
// SKIP_FORMAT: A special label that tells the logger not to apply any formatting.
// Useful for messages that are already formatted, like the server start message.
| 'SKIP_FORMAT';
Expand Down
11 changes: 2 additions & 9 deletions packages/astro/src/core/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,8 @@ function createContext({
set locals(_) {
throw new AstroError(AstroErrorData.LocalsReassigned);
},
get csp(): APIContext['csp'] {
return {
insertDirective() {},
insertScriptResource() {},
insertStyleResource() {},
insertScriptHash() {},
insertStyleHash() {},
};
},
session: undefined,
csp: undefined,
};
return Object.assign(context, {
getActionResult: createGetActionResult(context.locals),
Expand Down
53 changes: 16 additions & 37 deletions packages/astro/src/core/render-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ import type { RouteData, SSRResult } from '../types/public/internal.js';
import type { ServerIslandMappings, SSRActions } from './app/types.js';
import {
ASTRO_GENERATOR,
pipelineSymbol,
REROUTE_DIRECTIVE_HEADER,
REWRITE_DIRECTIVE_HEADER_KEY,
REWRITE_DIRECTIVE_HEADER_VALUE,
ROUTE_TYPE_HEADER,
pipelineSymbol,
responseSentSymbol,
} from './constants.js';
import { AstroCookies, attachCookiesToResponse } from './cookies/index.js';
import { getCookiesFromResponse } from './cookies/response.js';
import { pushDirective } from './csp/runtime.js';
import { generateCspDigest } from './encryption.js';
import { CspNotEnabled, ForbiddenRewrite } from './errors/errors-data.js';
import { ForbiddenRewrite } from './errors/errors-data.js';
import { AstroError, AstroErrorData } from './errors/index.js';
import { callMiddleware } from './middleware/callMiddleware.js';
import { sequence } from './middleware/index.js';
Expand Down Expand Up @@ -480,11 +480,15 @@ export class RenderContext {
return renderContext.session;
},
get csp(): APIContext['csp'] {
if (!pipeline.manifest.csp) {
pipeline.logger.warn(
'csp',
`context.csp was used when rendering the route ${colors.green(this.routePattern)}, but CSP was not configured. For more information, see https://docs.astro.build/en/reference/experimental-flags/csp/`,
);
return undefined;
}
return {
insertDirective(payload) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}
if (renderContext?.result?.directives) {
renderContext.result.directives = pushDirective(
renderContext.result.directives,
Expand All @@ -494,30 +498,16 @@ export class RenderContext {
renderContext?.result?.directives.push(payload);
}
},

insertScriptResource(resource) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}
renderContext.result?.scriptResources.push(resource);
},
insertStyleResource(resource) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}

renderContext.result?.styleResources.push(resource);
},
insertStyleHash(hash) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}
renderContext.result?.styleHashes.push(hash);
},
insertScriptHash(hash) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}
renderContext.result?.scriptHashes.push(hash);
},
};
Expand Down Expand Up @@ -745,12 +735,15 @@ export class RenderContext {
return getOriginPathname(renderContext.request);
},
get csp(): APIContext['csp'] {
if (!pipeline.manifest.csp) {
pipeline.logger.warn(
'csp',
`Astro.csp was used when rendering the route ${colors.green(this.routePattern)}, but CSP was not configured. For more information, see https://docs.astro.build/en/reference/experimental-flags/csp/`,
);
return undefined;
}
return {
insertDirective(payload) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}

if (renderContext?.result?.directives) {
renderContext.result.directives = pushDirective(
renderContext.result.directives,
Expand All @@ -760,30 +753,16 @@ export class RenderContext {
renderContext?.result?.directives.push(payload);
}
},

insertScriptResource(resource) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}
renderContext.result?.scriptResources.push(resource);
},
insertStyleResource(resource) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}

renderContext.result?.styleResources.push(resource);
},
insertStyleHash(hash) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}
renderContext.result?.styleHashes.push(hash);
},
insertScriptHash(hash) {
if (!pipeline.manifest.csp) {
throw new AstroError(CspNotEnabled);
}
renderContext.result?.scriptHashes.push(hash);
},
};
Expand Down
148 changes: 75 additions & 73 deletions packages/astro/src/types/public/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export interface APIContext<
*
* [Astro reference](https://docs.astro.build/en/reference/api-reference/#session)
*/
session?: AstroSession;
session: AstroSession | undefined;

/**
* A standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing information about the current request.
Expand Down Expand Up @@ -486,82 +486,84 @@ export interface APIContext<
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/)
*/
csp: {
/**
* It adds a specific CSP directive to the route being rendered.
*
* @param {CspDirective} directive - The directive to add to the current page.
*
* ## Example
*
* ```js
* ctx.insertDirective("default-src 'self' 'unsafe-inline' https://example.com")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertdirective)
*/
insertDirective: (directive: CspDirective) => void;
csp:
| {
/**
* It adds a specific CSP directive to the route being rendered.
*
* @param {CspDirective} directive - The directive to add to the current page.
*
* ## Example
*
* ```js
* ctx.insertDirective("default-src 'self' 'unsafe-inline' https://example.com")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertdirective)
*/
insertDirective: (directive: CspDirective) => void;

/**
* It set the resource for the directive `style-src` in the route being rendered. It overrides Astro's default.
*
* @param {string} payload - The source to insert in the `style-src` directive.
*
* ## Example
*
* ```js
* ctx.insertStyleResource("https://styles.cdn.example.com/")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertstyleresource)
*/
insertStyleResource: (payload: string) => void;
/**
* It set the resource for the directive `style-src` in the route being rendered. It overrides Astro's default.
*
* @param {string} payload - The source to insert in the `style-src` directive.
*
* ## Example
*
* ```js
* ctx.insertStyleResource("https://styles.cdn.example.com/")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertstyleresource)
*/
insertStyleResource: (payload: string) => void;

/**
* Insert a single style hash to the route being rendered.
*
* @param {CspHash} hash - The hash to insert in the `style-src` directive.
*
* ## Example
*
* ```js
* ctx.insertStyleHash("sha256-1234567890abcdef1234567890")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertstylehash)
*/
insertStyleHash: (hash: CspHash) => void;
/**
* Insert a single style hash to the route being rendered.
*
* @param {CspHash} hash - The hash to insert in the `style-src` directive.
*
* ## Example
*
* ```js
* ctx.insertStyleHash("sha256-1234567890abcdef1234567890")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertstylehash)
*/
insertStyleHash: (hash: CspHash) => void;

/**
* It set the resource for the directive `script-src` in the route being rendered.
*
* @param {string} resource - The source to insert in the `script-src` directive.
*
* ## Example
*
* ```js
* ctx.insertScriptResource("https://scripts.cdn.example.com/")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertscriptresource)
*/
insertScriptResource: (resource: string) => void;
/**
* It set the resource for the directive `script-src` in the route being rendered.
*
* @param {string} resource - The source to insert in the `script-src` directive.
*
* ## Example
*
* ```js
* ctx.insertScriptResource("https://scripts.cdn.example.com/")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertscriptresource)
*/
insertScriptResource: (resource: string) => void;

/**
* Insert a single script hash to the route being rendered.
*
* @param {CspHash} hash - The hash to insert in the `script-src` directive.
*
* ## Example
*
* ```js
* ctx.insertScriptHash("sha256-1234567890abcdef1234567890")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertscripthash)
*/
insertScriptHash: (hash: CspHash) => void;
};
/**
* Insert a single script hash to the route being rendered.
*
* @param {CspHash} hash - The hash to insert in the `script-src` directive.
*
* ## Example
*
* ```js
* ctx.insertScriptHash("sha256-1234567890abcdef1234567890")
* ```
*
* [Astro reference](https://docs.astro.build/en/reference/experimental-flags/csp/#cspinsertscripthash)
*/
insertScriptHash: (hash: CspHash) => void;
}
| undefined;

/**
* The route currently rendered. It's stripped of the `srcDir` and the `pages` folder, and it doesn't contain the extension.
Expand Down
Loading