Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nonce-based csp to style #305

Open
wants to merge 1 commit into
base: rc
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion proxy/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getLoaderVersion,
setLogLevel,
createRoute,
generateRandom,
} from './utils'
import { CustomerVariables } from './utils/customer-variables/customer-variables'
import { HeaderCustomerVariables } from './utils/customer-variables/header-customer-variables'
Expand Down Expand Up @@ -112,7 +113,7 @@ function handleStatusPage(
_: CloudFrontRequest,
customerVariables: CustomerVariables
): Promise<CloudFrontResultResponse> {
return handleStatus(customerVariables)
return handleStatus(customerVariables, generateRandom())
}

export const handler = async (event: CloudFrontRequestEvent): Promise<CloudFrontResultResponse> => {
Expand Down
21 changes: 16 additions & 5 deletions proxy/handlers/handleStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface EnvVarInfo {
export interface StatusInfo {
version: string
envInfo: EnvVarInfo[]
styleNonce: string
}

async function getEnvInfo(customerVariables: CustomerVariables) {
Expand Down Expand Up @@ -64,13 +65,13 @@ function renderEnvInfo(envInfo: EnvVarInfo[]) {
`
}

function renderHtml({ version, envInfo }: StatusInfo) {
function renderHtml({ version, envInfo, styleNonce }: StatusInfo) {
return `
<html lang="en-US">
<head>
<title>CloudFront integration status</title>
<meta charset="utf-8">
<style>
<style nonce='${styleNonce}'>
body, .env-info {
display: flex;
}
Expand Down Expand Up @@ -99,21 +100,31 @@ function renderHtml({ version, envInfo }: StatusInfo) {
`
}

export async function getStatusInfo(customerVariables: CustomerVariables): Promise<StatusInfo> {
export async function getStatusInfo(customerVariables: CustomerVariables, styleNonce: string): Promise<StatusInfo> {
return {
version: '__lambda_func_version__',
envInfo: await getEnvInfo(customerVariables),
styleNonce,
}
}

export async function handleStatus(customerVariables: CustomerVariables): Promise<CloudFrontResultResponse> {
const body = await getStatusInfo(customerVariables)
export async function handleStatus(
customerVariables: CustomerVariables,
styleNonce: string
): Promise<CloudFrontResultResponse> {
const body = await getStatusInfo(customerVariables, styleNonce)

return {
status: '200',
body: renderHtml(body).trim(),
headers: {
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
'content-security-policy': [
{
key: 'Content-Security-Policy',
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
},
],
},
}
}
9 changes: 6 additions & 3 deletions proxy/test/handlers/__snapshots__/handleStatus.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ exports[`Get status info returns correct response with empty non obfuscated vari
"value": "api.fpjs.io",
},
],
"styleNonce": "hardcodedStyleNonce",
"version": "__lambda_func_version__",
}
`;
Expand Down Expand Up @@ -82,6 +83,7 @@ exports[`Get status info returns correct response with empty pre shared secret 1
"value": "api.fpjs.io",
},
],
"styleNonce": "hardcodedStyleNonce",
"version": "__lambda_func_version__",
}
`;
Expand Down Expand Up @@ -125,6 +127,7 @@ exports[`Get status info returns correct status info 1`] = `
"value": "api.fpjs.io",
},
],
"styleNonce": "hardcodedStyleNonce",
"version": "__lambda_func_version__",
}
`;
Expand All @@ -134,7 +137,7 @@ exports[`Handle status returns correct status info in html if all variables are
<head>
<title>CloudFront integration status</title>
<meta charset="utf-8">
<style>
<style nonce='hardcodedStyleNonce'>
body, .env-info {
display: flex;
}
Expand Down Expand Up @@ -171,7 +174,7 @@ exports[`Handle status returns correct status info in html if some variables are
<head>
<title>CloudFront integration status</title>
<meta charset="utf-8">
<style>
<style nonce='hardcodedStyleNonce'>
body, .env-info {
display: flex;
}
Expand Down Expand Up @@ -211,7 +214,7 @@ exports[`Handle status returns correct status info in html if some variables are
<head>
<title>CloudFront integration status</title>
<meta charset="utf-8">
<style>
<style nonce='hardcodedStyleNonce'>
body, .env-info {
display: flex;
}
Expand Down
32 changes: 26 additions & 6 deletions proxy/test/handlers/handleStatus.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ import { getInMemoryCustomerVariables } from '../utils/customer-variables/in-mem
import { CustomerVariableType } from '../../utils/customer-variables/types'
import { getStatusInfo, handleStatus } from '../../handlers/handleStatus'

const styleNonce = 'hardcodedStyleNonce'

describe('Handle status', () => {
it('returns correct status info in html if all variables are set', async () => {
const { customerVariables } = getInMemoryCustomerVariables()

const result = await handleStatus(customerVariables)
const result = await handleStatus(customerVariables, styleNonce)

expect(result.headers).toEqual({
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
'content-security-policy': [
{
key: 'Content-Security-Policy',
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
},
],
})

expect(result.body).toMatchSnapshot()
Expand All @@ -22,10 +30,16 @@ describe('Handle status', () => {
variables.fpjs_agent_download_path = null
variables.fpjs_get_result_path = null

const result = await handleStatus(customerVariables)
const result = await handleStatus(customerVariables, styleNonce)

expect(result.headers).toEqual({
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
'content-security-policy': [
{
key: 'Content-Security-Policy',
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
},
],
})

expect(result.body).toMatchSnapshot()
Expand All @@ -36,10 +50,16 @@ describe('Handle status', () => {

variables.fpjs_pre_shared_secret = null

const result = await handleStatus(customerVariables)
const result = await handleStatus(customerVariables, styleNonce)

expect(result.headers).toEqual({
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
'content-security-policy': [
{
key: 'Content-Security-Policy',
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
},
],
})

expect(result.body).toMatchSnapshot()
Expand All @@ -50,7 +70,7 @@ describe('Get status info', () => {
it('returns correct status info', async () => {
const { customerVariables } = getInMemoryCustomerVariables()

const result = await getStatusInfo(customerVariables)
const result = await getStatusInfo(customerVariables, styleNonce)

expect(result).toMatchSnapshot()
})
Expand All @@ -59,15 +79,15 @@ describe('Get status info', () => {
const { customerVariables, variables } = getInMemoryCustomerVariables()
variables[CustomerVariableType.PreSharedSecret] = null

const result = await getStatusInfo(customerVariables)
const result = await getStatusInfo(customerVariables, styleNonce)

expect(result).toMatchSnapshot()
})

it('returns correct response with empty non obfuscated variable', async () => {
const { customerVariables } = getInMemoryCustomerVariables()

const result = await getStatusInfo(customerVariables)
const result = await getStatusInfo(customerVariables, styleNonce)

expect(result).toMatchSnapshot()
})
Expand Down
2 changes: 2 additions & 0 deletions proxy/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
addEndingTrailingSlashToRoute,
} from './routing'
import { setLogLevel } from './log'
import { generateRandom } from './string'

export {
getAgentUri,
Expand All @@ -43,4 +44,5 @@ export {
addPathnameMatchBeforeRoute,
addEndingTrailingSlashToRoute,
setLogLevel,
generateRandom,
}
9 changes: 9 additions & 0 deletions proxy/utils/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function generateRandom() {

Check warning on line 1 in proxy/utils/string.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
let result = ''

Check warning on line 2 in proxy/utils/string.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'

Check warning on line 3 in proxy/utils/string.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
const indices = crypto.getRandomValues(new Uint8Array(24))

Check warning on line 4 in proxy/utils/string.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
for (const index of indices) {
result += characters[index % characters.length]

Check warning on line 6 in proxy/utils/string.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 7 in proxy/utils/string.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
return btoa(result)

Check warning on line 8 in proxy/utils/string.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}
Loading