Skip to content

Commit

Permalink
Safelist embedded cdn script urls via <meta tag in html, and use a no…
Browse files Browse the repository at this point in the history
…nce (sha256 of a uuid) to validate script tags in <meta
  • Loading branch information
mayakoneval committed May 2, 2023
1 parent 8101896 commit c097098
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const getEmbeddedExplorerHTML = (
explorerCdnVersion: string,
config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions,
apolloServerVersion: string,
nonce: string,
) => {
interface EmbeddableExplorerOptions {
graphRef: string;
Expand Down Expand Up @@ -96,12 +97,12 @@ export const getEmbeddedExplorerHTML = (
style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableExplorer"
></div>
<script src="https://embeddable-explorer.cdn.apollographql.com/${encodeURIComponent(
<script nonce="${nonce}" src="https://embeddable-explorer.cdn.apollographql.com/${encodeURIComponent(
explorerCdnVersion,
)}/embeddable-explorer.umd.production.min.js?runtime=${encodeURIComponent(
apolloServerVersion,
)}"></script>
<script>
<script nonce="${nonce}">
var endpointUrl = window.location.href;
var embeddedExplorerConfig = ${getConfigStringForHtml(
embeddedExplorerParams,
Expand All @@ -118,6 +119,7 @@ export const getEmbeddedSandboxHTML = (
sandboxCdnVersion: string,
config: ApolloServerPluginEmbeddedLandingPageLocalDefaultOptions,
apolloServerVersion: string,
nonce: string,
) => {
const endpointIsEditable =
typeof config.embed === 'boolean'
Expand All @@ -139,12 +141,12 @@ export const getEmbeddedSandboxHTML = (
style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableSandbox"
></div>
<script src="https://embeddable-sandbox.cdn.apollographql.com/${encodeURIComponent(
<script nonce="${nonce}" src="https://embeddable-sandbox.cdn.apollographql.com/${encodeURIComponent(
sandboxCdnVersion,
)}/embeddable-sandbox.umd.production.min.js?runtime=${encodeURIComponent(
apolloServerVersion,
)}"></script>
<script>
<script nonce="${nonce}">
var initialEndpoint = window.location.href;
new window.EmbeddedSandbox({
target: '#embeddableSandbox',
Expand Down
44 changes: 36 additions & 8 deletions packages/server/src/plugin/landingPage/default/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
getEmbeddedSandboxHTML,
} from './getEmbeddedHTML.js';
import { packageVersion } from '../../../generated/packageVersion.js';
import { createHash } from '@apollo/utils.createhash';
import { v4 as uuidv4 } from 'uuid';

export type {
ApolloServerPluginLandingPageLocalDefaultOptions,
Expand Down Expand Up @@ -61,6 +63,7 @@ const getNonEmbeddedLandingPageHTML = (
cdnVersion: string,
config: LandingPageConfig,
apolloServerVersion: string,
nonce: string,
) => {
const encodedConfig = encodeConfig(config);

Expand All @@ -70,7 +73,9 @@ const getNonEmbeddedLandingPageHTML = (
<p>The full landing page cannot be loaded; it appears that you might be offline.</p>
</div>
<script>window.landingPage = ${encodedConfig};</script>
<script src="https://apollo-server-landing-page.cdn.apollographql.com/${cdnVersion}/static/js/main.js?runtime=${apolloServerVersion}"></script>`;
<script nonce="${nonce}" src="https://apollo-server-landing-page.cdn.apollographql.com/${encodeURIComponent(
cdnVersion,
)}/static/js/main.js?runtime=${apolloServerVersion}"></script>`;
};

// Helper for the two actual plugin functions.
Expand All @@ -84,6 +89,8 @@ function ApolloServerPluginLandingPageDefault<TContext extends BaseContext>(
const version = maybeVersion ?? '_latest';
const apolloServerVersion = `@apollo/server@${packageVersion}`;

const nonce = createHash('sha256').update(uuidv4()).digest('hex');

return {
__internal_installed_implicitly__: false,
async serverWillStart() {
Expand All @@ -94,9 +101,16 @@ function ApolloServerPluginLandingPageDefault<TContext extends BaseContext>(
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'nonce-${nonce}' https://apollo-server-landing-page.cdn.apollographql.com/${version}/static/js/main.js https://embeddable-sandbox.cdn.apollographql.com/${encodeURIComponent(
version,
)}/embeddable-sandbox.umd.production.min.js https://embeddable-explorer.cdn.apollographql.com/${encodeURIComponent(
version,
)}/embeddable-explorer.umd.production.min.js" />
<link
rel="icon"
href="https://apollo-server-landing-page.cdn.apollographql.com/${version}/assets/favicon.png"
href="https://apollo-server-landing-page.cdn.apollographql.com/${encodeURIComponent(
version,
)}/assets/favicon.png"
/>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
Expand All @@ -108,11 +122,15 @@ function ApolloServerPluginLandingPageDefault<TContext extends BaseContext>(
<meta name="description" content="Apollo server landing page" />
<link
rel="apple-touch-icon"
href="https://apollo-server-landing-page.cdn.apollographql.com/${version}/assets/favicon.png"
href="https://apollo-server-landing-page.cdn.apollographql.com/${encodeURIComponent(
version,
)}/assets/favicon.png"
/>
<link
rel="manifest"
href="https://apollo-server-landing-page.cdn.apollographql.com/${version}/manifest.json"
href="https://apollo-server-landing-page.cdn.apollographql.com/${encodeURIComponent(
version,
)}/manifest.json"
/>
<title>Apollo Server</title>
</head>
Expand All @@ -135,11 +153,21 @@ function ApolloServerPluginLandingPageDefault<TContext extends BaseContext>(
${
config.embed
? 'graphRef' in config && config.graphRef
? getEmbeddedExplorerHTML(version, config, apolloServerVersion)
? getEmbeddedExplorerHTML(version, config, apolloServerVersion, nonce)
: !('graphRef' in config)
? getEmbeddedSandboxHTML(version, config, apolloServerVersion)
: getNonEmbeddedLandingPageHTML(version, config, apolloServerVersion)
: getNonEmbeddedLandingPageHTML(version, config, apolloServerVersion)
? getEmbeddedSandboxHTML(version, config, apolloServerVersion, nonce)
: getNonEmbeddedLandingPageHTML(
version,
config,
apolloServerVersion,
nonce,
)
: getNonEmbeddedLandingPageHTML(
version,
config,
apolloServerVersion,
nonce,
)
}
</div>
</body>
Expand Down

0 comments on commit c097098

Please sign in to comment.