Skip to content

Commit fa659e6

Browse files
authored
merge: feature/add-style-nonce
Add nonce-based csp to style
2 parents 2b58661 + e2b5f6c commit fa659e6

File tree

6 files changed

+61
-15
lines changed

6 files changed

+61
-15
lines changed

proxy/app.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
getLoaderVersion,
1414
setLogLevel,
1515
createRoute,
16+
generateRandom,
1617
} from './utils'
1718
import { CustomerVariables } from './utils/customer-variables/customer-variables'
1819
import { HeaderCustomerVariables } from './utils/customer-variables/header-customer-variables'
@@ -112,7 +113,7 @@ function handleStatusPage(
112113
_: CloudFrontRequest,
113114
customerVariables: CustomerVariables
114115
): Promise<CloudFrontResultResponse> {
115-
return handleStatus(customerVariables)
116+
return handleStatus(customerVariables, generateRandom())
116117
}
117118

118119
export const handler = async (event: CloudFrontRequestEvent): Promise<CloudFrontResultResponse> => {

proxy/handlers/handleStatus.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface EnvVarInfo {
1515
export interface StatusInfo {
1616
version: string
1717
envInfo: EnvVarInfo[]
18+
styleNonce: string
1819
}
1920

2021
async function getEnvInfo(customerVariables: CustomerVariables) {
@@ -64,13 +65,13 @@ function renderEnvInfo(envInfo: EnvVarInfo[]) {
6465
`
6566
}
6667

67-
function renderHtml({ version, envInfo }: StatusInfo) {
68+
function renderHtml({ version, envInfo, styleNonce }: StatusInfo) {
6869
return `
6970
<html lang="en-US">
7071
<head>
7172
<title>CloudFront integration status</title>
7273
<meta charset="utf-8">
73-
<style>
74+
<style nonce='${styleNonce}'>
7475
body, .env-info {
7576
display: flex;
7677
}
@@ -99,21 +100,31 @@ function renderHtml({ version, envInfo }: StatusInfo) {
99100
`
100101
}
101102

102-
export async function getStatusInfo(customerVariables: CustomerVariables): Promise<StatusInfo> {
103+
export async function getStatusInfo(customerVariables: CustomerVariables, styleNonce: string): Promise<StatusInfo> {
103104
return {
104105
version: '__lambda_func_version__',
105106
envInfo: await getEnvInfo(customerVariables),
107+
styleNonce,
106108
}
107109
}
108110

109-
export async function handleStatus(customerVariables: CustomerVariables): Promise<CloudFrontResultResponse> {
110-
const body = await getStatusInfo(customerVariables)
111+
export async function handleStatus(
112+
customerVariables: CustomerVariables,
113+
styleNonce: string
114+
): Promise<CloudFrontResultResponse> {
115+
const body = await getStatusInfo(customerVariables, styleNonce)
111116

112117
return {
113118
status: '200',
114119
body: renderHtml(body).trim(),
115120
headers: {
116121
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
122+
'content-security-policy': [
123+
{
124+
key: 'Content-Security-Policy',
125+
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
126+
},
127+
],
117128
},
118129
}
119130
}

proxy/test/handlers/__snapshots__/handleStatus.test.ts.snap

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ exports[`Get status info returns correct response with empty non obfuscated vari
3939
"value": "api.fpjs.io",
4040
},
4141
],
42+
"styleNonce": "hardcodedStyleNonce",
4243
"version": "__lambda_func_version__",
4344
}
4445
`;
@@ -82,6 +83,7 @@ exports[`Get status info returns correct response with empty pre shared secret 1
8283
"value": "api.fpjs.io",
8384
},
8485
],
86+
"styleNonce": "hardcodedStyleNonce",
8587
"version": "__lambda_func_version__",
8688
}
8789
`;
@@ -125,6 +127,7 @@ exports[`Get status info returns correct status info 1`] = `
125127
"value": "api.fpjs.io",
126128
},
127129
],
130+
"styleNonce": "hardcodedStyleNonce",
128131
"version": "__lambda_func_version__",
129132
}
130133
`;
@@ -134,7 +137,7 @@ exports[`Handle status returns correct status info in html if all variables are
134137
<head>
135138
<title>CloudFront integration status</title>
136139
<meta charset="utf-8">
137-
<style>
140+
<style nonce='hardcodedStyleNonce'>
138141
body, .env-info {
139142
display: flex;
140143
}
@@ -171,7 +174,7 @@ exports[`Handle status returns correct status info in html if some variables are
171174
<head>
172175
<title>CloudFront integration status</title>
173176
<meta charset="utf-8">
174-
<style>
177+
<style nonce='hardcodedStyleNonce'>
175178
body, .env-info {
176179
display: flex;
177180
}
@@ -211,7 +214,7 @@ exports[`Handle status returns correct status info in html if some variables are
211214
<head>
212215
<title>CloudFront integration status</title>
213216
<meta charset="utf-8">
214-
<style>
217+
<style nonce='hardcodedStyleNonce'>
215218
body, .env-info {
216219
display: flex;
217220
}

proxy/test/handlers/handleStatus.test.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@ import { getInMemoryCustomerVariables } from '../utils/customer-variables/in-mem
22
import { CustomerVariableType } from '../../utils/customer-variables/types'
33
import { getStatusInfo, handleStatus } from '../../handlers/handleStatus'
44

5+
const styleNonce = 'hardcodedStyleNonce'
6+
57
describe('Handle status', () => {
68
it('returns correct status info in html if all variables are set', async () => {
79
const { customerVariables } = getInMemoryCustomerVariables()
810

9-
const result = await handleStatus(customerVariables)
11+
const result = await handleStatus(customerVariables, styleNonce)
1012

1113
expect(result.headers).toEqual({
1214
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
15+
'content-security-policy': [
16+
{
17+
key: 'Content-Security-Policy',
18+
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
19+
},
20+
],
1321
})
1422

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

25-
const result = await handleStatus(customerVariables)
33+
const result = await handleStatus(customerVariables, styleNonce)
2634

2735
expect(result.headers).toEqual({
2836
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
37+
'content-security-policy': [
38+
{
39+
key: 'Content-Security-Policy',
40+
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
41+
},
42+
],
2943
})
3044

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

3751
variables.fpjs_pre_shared_secret = null
3852

39-
const result = await handleStatus(customerVariables)
53+
const result = await handleStatus(customerVariables, styleNonce)
4054

4155
expect(result.headers).toEqual({
4256
'content-type': [{ key: 'Content-Type', value: 'text/html' }],
57+
'content-security-policy': [
58+
{
59+
key: 'Content-Security-Policy',
60+
value: `default-src 'none'; img-src https://fingerprint.com; style-src 'nonce-${styleNonce}'`,
61+
},
62+
],
4363
})
4464

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

53-
const result = await getStatusInfo(customerVariables)
73+
const result = await getStatusInfo(customerVariables, styleNonce)
5474

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

62-
const result = await getStatusInfo(customerVariables)
82+
const result = await getStatusInfo(customerVariables, styleNonce)
6383

6484
expect(result).toMatchSnapshot()
6585
})
6686

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

70-
const result = await getStatusInfo(customerVariables)
90+
const result = await getStatusInfo(customerVariables, styleNonce)
7191

7292
expect(result).toMatchSnapshot()
7393
})

proxy/utils/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
addEndingTrailingSlashToRoute,
2121
} from './routing'
2222
import { setLogLevel } from './log'
23+
import { generateRandom } from './string'
2324

2425
export {
2526
getAgentUri,
@@ -43,4 +44,5 @@ export {
4344
addPathnameMatchBeforeRoute,
4445
addEndingTrailingSlashToRoute,
4546
setLogLevel,
47+
generateRandom,
4648
}

proxy/utils/string.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function generateRandom() {
2+
let result = ''
3+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
4+
const indices = crypto.getRandomValues(new Uint8Array(24))
5+
for (const index of indices) {
6+
result += characters[index % characters.length]
7+
}
8+
return btoa(result)
9+
}

0 commit comments

Comments
 (0)