Skip to content

Commit 411b1b6

Browse files
authored
Merge branch 'main' into feat/df-623-payment
2 parents 4098209 + ca6417d commit 411b1b6

6 files changed

Lines changed: 106 additions & 11 deletions

File tree

package-lock.json

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@defra/forms-engine-plugin",
3-
"version": "4.0.38",
3+
"version": "4.0.39",
44
"description": "Defra forms engine",
55
"type": "module",
66
"files": [
@@ -108,6 +108,7 @@
108108
"lodash": "^4.17.21",
109109
"marked": "^15.0.12",
110110
"nunjucks": "^3.2.4",
111+
"obscenity": "^0.4.5",
111112
"outdent": "^0.8.0",
112113
"pino": "^9.14.0",
113114
"pino-pretty": "^13.1.2",

src/server/plugins/engine/pageControllers/StatusPageController.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import {
1212
export class StatusPageController extends QuestionPageController {
1313
declare pageDef: PageStatus
1414
allowSaveAndExit = false
15+
showReferenceNumber = false
1516

1617
constructor(model: FormModel, pageDef: PageStatus) {
1718
super(model, pageDef)
1819
this.viewName = 'confirmation'
20+
this.showReferenceNumber = model.def.options?.showReferenceNumber ?? false
1921
}
2022

2123
getRelevantPath() {
@@ -54,7 +56,9 @@ export class StatusPageController extends QuestionPageController {
5456
return h.view(viewName, {
5557
...viewModel,
5658
submissionGuidance,
57-
formName
59+
formName,
60+
showReferenceNumber: this.showReferenceNumber,
61+
referenceNumber: context.referenceNumber
5862
})
5963
}
6064
}

src/server/plugins/engine/referenceNumbers.test.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'
1+
import {
2+
convertToDecAlpha,
3+
generateUniqueReference
4+
} from '~/src/server/plugins/engine/referenceNumbers.js'
25

36
describe('generateUniqueReference', () => {
47
it('should generate a reference number with 3 segments when no prefix is provided', () => {
@@ -30,4 +33,42 @@ describe('generateUniqueReference', () => {
3033
const referenceNumber2 = generateUniqueReference()
3134
expect(referenceNumber1).not.toBe(referenceNumber2)
3235
})
36+
37+
describe('convertToDecAlpha', () => {
38+
it('should generate correct characters in string', () => {
39+
const allValuesHexPairs = Array.from(Array(256).keys())
40+
expect(convertToDecAlpha(allValuesHexPairs)).toBe(
41+
'AAAAAAAAA' +
42+
'BBBBBBBBB' +
43+
'CCCCCCCC' +
44+
'DDDDDDDDD' +
45+
'EEEEEEEE' +
46+
'FFFFFFFFF' +
47+
'HHHHHHHH' +
48+
'JJJJJJJJJ' +
49+
'KKKKKKKK' +
50+
'LLLLLLLLL' +
51+
'MMMMMMMM' +
52+
'NNNNNNNNN' +
53+
'PPPPPPPP' +
54+
'RRRRRRRRR' +
55+
'SSSSSSSS' +
56+
'TTTTTTTTT' +
57+
'UUUUUUUUU' +
58+
'VVVVVVVV' +
59+
'WWWWWWWWW' +
60+
'XXXXXXXX' +
61+
'YYYYYYYYY' +
62+
'ZZZZZZZZ' +
63+
'222222222' +
64+
'33333333' +
65+
'444444444' +
66+
'55555555' +
67+
'666666666' +
68+
'77777777' +
69+
'888888888' +
70+
'99999999'
71+
)
72+
})
73+
})
3374
})
Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,56 @@
11
import { randomBytes } from 'node:crypto'
22

3+
import {
4+
RegExpMatcher,
5+
englishDataset,
6+
englishRecommendedTransformers
7+
} from 'obscenity'
8+
9+
/**
10+
* To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed.
11+
* @param strCodes - array of binary input values
12+
*/
13+
export function convertToDecAlpha(strCodes: number[]) {
14+
const validChars = 'ABCDEFHJKLMNPRSTUVWXYZ23456789'
15+
const strLen = validChars.length
16+
const outArray = [] as string[]
17+
18+
strCodes.forEach((code) => {
19+
const pos = (code / 256) * strLen
20+
outArray.push(validChars.charAt(pos))
21+
})
22+
23+
return outArray.join('')
24+
}
25+
326
/**
427
* Generates a reference number in the format of `XXX-XXX-XXX`, or `PREFIX-XXX-XXX` if a prefix is provided.
528
* Provides no guarantee on uniqueness.
29+
* To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed
30+
* (see https://gunkies.org/wiki/DEC_alphabet )
631
*/
732
export function generateUniqueReference(prefix?: string) {
833
const segmentLength = 3
934
const segmentCount = prefix ? 2 : 3
1035
prefix = prefix ? `${prefix}-` : ''
1136

12-
const segments = Array.from(
13-
{ length: segmentCount },
14-
() => randomBytes(segmentLength).toString('hex').slice(0, segmentLength) // 0-9a-f, might be good enough?
15-
)
37+
const profanityMatcher = new RegExpMatcher({
38+
...englishDataset.build(),
39+
...englishRecommendedTransformers
40+
})
41+
42+
let referenceNumber
43+
44+
do {
45+
const segments = Array.from({ length: segmentCount }, () =>
46+
convertToDecAlpha([...randomBytes(segmentLength)]).slice(
47+
0,
48+
segmentLength * 2
49+
)
50+
)
51+
52+
referenceNumber = `${prefix}${segments.join('-')}`.toUpperCase()
53+
} while (profanityMatcher.hasMatch(referenceNumber.replaceAll('-', '')))
1654

17-
return `${prefix}${segments.join('-')}`.toUpperCase()
55+
return referenceNumber
1856
}

src/server/plugins/engine/views/confirmation.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
<div class="govuk-grid-row">
99
<div class="govuk-grid-column-two-thirds">
1010
{{ govukPanel({
11-
titleText: pageTitle
11+
titleText: pageTitle,
12+
html: "Your reference number<br><strong>" + referenceNumber + "</strong>" if showReferenceNumber
1213
}) }}
1314
<h2 class="govuk-heading-m">What happens next</h2>
1415
<div class="app-prose-scope">

0 commit comments

Comments
 (0)