Skip to content

Commit 411f78d

Browse files
authored
Merge pull request #2011 from bartvanandel/feat/1216-1531-upgrade-uuid
2 parents 5d33531 + 7ed7fca commit 411f78d

File tree

6 files changed

+138
-24
lines changed

6 files changed

+138
-24
lines changed

package-lock.json

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

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@
182182
"ua-parser-js": "^1.0.38",
183183
"unorm": "^1.6.0",
184184
"utf8": "^3.0.0",
185+
"uuid": "^11.1.0",
185186
"vkbeautify": "^0.99.3",
186187
"xpath": "0.0.34",
187188
"xregexp": "^5.1.1",

src/core/config/Categories.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@
550550
"Pseudo-Random Number Generator",
551551
"Generate De Bruijn Sequence",
552552
"Generate UUID",
553+
"Analyse UUID",
553554
"Generate TOTP",
554555
"Generate HOTP",
555556
"Generate QR Code",

src/core/operations/AnalyseUUID.mjs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @author n1474335 [[email protected]]
3+
* @copyright Crown Copyright 2023
4+
* @license Apache-2.0
5+
*/
6+
7+
import * as uuid from "uuid";
8+
9+
import Operation from "../Operation.mjs";
10+
import OperationError from "../errors/OperationError.mjs";
11+
12+
/**
13+
* Analyse UUID operation
14+
*/
15+
class AnalyseUUID extends Operation {
16+
17+
/**
18+
* AnalyseUUID constructor
19+
*/
20+
constructor() {
21+
super();
22+
23+
this.name = "Analyse UUID";
24+
this.module = "Crypto";
25+
this.description = "Tries to determine information about a given UUID and suggests which version may have been used to generate it";
26+
this.infoURL = "https://wikipedia.org/wiki/Universally_unique_identifier";
27+
this.inputType = "string";
28+
this.outputType = "string";
29+
this.args = [];
30+
}
31+
32+
/**
33+
* @param {string} input
34+
* @param {Object[]} args
35+
* @returns {string}
36+
*/
37+
run(input, args) {
38+
try {
39+
const uuidVersion = uuid.version(input);
40+
return "UUID version: " + uuidVersion;
41+
} catch (error) {
42+
throw new OperationError("Invalid UUID");
43+
}
44+
}
45+
46+
}
47+
48+
export default AnalyseUUID;

src/core/operations/GenerateUUID.mjs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
*/
66

77
import Operation from "../Operation.mjs";
8-
import crypto from "crypto";
9-
8+
import * as uuid from "uuid";
9+
import OperationError from "../errors/OperationError.mjs";
1010
/**
1111
* Generate UUID operation
1212
*/
@@ -20,11 +20,38 @@ class GenerateUUID extends Operation {
2020

2121
this.name = "Generate UUID";
2222
this.module = "Crypto";
23-
this.description = "Generates an RFC 4122 version 4 compliant Universally Unique Identifier (UUID), also known as a Globally Unique Identifier (GUID).<br><br>A version 4 UUID relies on random numbers, in this case generated using <code>window.crypto</code> if available and falling back to <code>Math.random</code> if not.";
23+
this.description =
24+
"Generates an RFC 9562 (formerly RFC 4122) compliant Universally Unique Identifier (UUID), " +
25+
"also known as a Globally Unique Identifier (GUID).<br>" +
26+
"<br>" +
27+
"We currently support generating the following UUID versions:<br>" +
28+
"<ul>" +
29+
"<li><strong>v1</strong>: Timestamp-based</li>" +
30+
"<li><strong>v3</strong>: Namespace w/ MD5</li>" +
31+
"<li><strong>v4</strong>: Random (default)</li>" +
32+
"<li><strong>v5</strong>: Namespace w/ SHA-1</li>" +
33+
"<li><strong>v6</strong>: Timestamp, reordered</li>" +
34+
"<li><strong>v7</strong>: Unix Epoch time-based</li>" +
35+
"</ul>" +
36+
"UUIDs are generated using the <a href='https://npmjs.org/uuid/'><code>uuid</code><a> package.<br>";
2437
this.infoURL = "https://wikipedia.org/wiki/Universally_unique_identifier";
2538
this.inputType = "string";
2639
this.outputType = "string";
27-
this.args = [];
40+
this.args = [
41+
{
42+
name: "Version",
43+
hint: "UUID version",
44+
type: "option",
45+
value: ["v1", "v3", "v4", "v5", "v6", "v7"],
46+
defaultIndex: 2,
47+
},
48+
{
49+
name: "Namespace",
50+
hint: "UUID namespace (UUID; valid for v3 and v5)",
51+
type: "string",
52+
value: "1b671a64-40d5-491e-99b0-da01ff1f3341"
53+
}
54+
];
2855
}
2956

3057
/**
@@ -33,16 +60,17 @@ class GenerateUUID extends Operation {
3360
* @returns {string}
3461
*/
3562
run(input, args) {
36-
const buf = new Uint32Array(4).map(() => {
37-
return crypto.randomBytes(4).readUInt32BE(0, true);
38-
});
39-
let i = 0;
40-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
41-
const r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
42-
v = c === "x" ? r : (r & 0x3 | 0x8);
43-
i++;
44-
return v.toString(16);
45-
});
63+
const [version, namespace] = args;
64+
const hasDesiredVersion = typeof uuid[version] === "function";
65+
if (!hasDesiredVersion) throw new OperationError("Invalid UUID version");
66+
67+
const requiresNamespace = ["v3", "v5"].includes(version);
68+
if (!requiresNamespace) return uuid[version]();
69+
70+
const hasValidNamespace = typeof namespace === "string" && uuid.validate(namespace);
71+
if (!hasValidNamespace) throw new OperationError("Invalid UUID namespace");
72+
73+
return uuid[version](input, namespace);
4674
}
4775

4876
}

tests/node/tests/operations.mjs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -580,10 +580,25 @@ Password: 282760`;
580580
assert.strictEqual(result.toString().substr(0, 37), "-----BEGIN PGP PRIVATE KEY BLOCK-----");
581581
}),
582582

583-
it("Generate UUID", () => {
584-
const result = chef.generateUUID();
585-
assert.ok(result.toString());
586-
assert.strictEqual(result.toString().length, 36);
583+
...[1, 3, 4, 5, 6, 7].map(version => it(`Generate UUID v${version}`, () => {
584+
const result = chef.generateUUID("", { "version": `v${version}` }).toString();
585+
assert.ok(result);
586+
assert.strictEqual(result.length, 36);
587+
})),
588+
589+
...[1, 3, 4, 5, 6, 7].map(version => it(`Analyze UUID v${version}`, () => {
590+
const uuid = chef.generateUUID("", { "version": `v${version}` }).toString();
591+
const result = chef.analyseUUID(uuid).toString();
592+
const expected = `UUID version: ${version}`;
593+
assert.strictEqual(result, expected);
594+
})),
595+
596+
it("Generate UUID using defaults", () => {
597+
const uuid = chef.generateUUID();
598+
assert.ok(uuid);
599+
600+
const analysis = chef.analyseUUID(uuid).toString();
601+
assert.strictEqual(analysis, "UUID version: 4");
587602
}),
588603

589604
it("Gzip, Gunzip", () => {

0 commit comments

Comments
 (0)