Skip to content

Commit 2cf5727

Browse files
authored
Merge pull request #193 from bcgov/AI-1392
AI-1392 Security and Vulnerability Round Up - June 2026
2 parents d0f10ea + 6e319a3 commit 2cf5727

13 files changed

Lines changed: 5941 additions & 4625 deletions

File tree

apps/backend-services/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"@prisma/client": "7.2.0",
5454
"@temporalio/client": "1.10.0",
5555
"ajv": "8.18.0",
56-
"axios": "1.15.0",
56+
"axios": "1.17.0",
5757
"bcrypt": "6.0.0",
5858
"body-parser": "1.20.3",
5959
"class-transformer": "0.5.1",
@@ -71,6 +71,7 @@
7171
"pg": "8.16.3",
7272
"prisma": "7.2.0",
7373
"prom-client": "15.1.3",
74+
"reflect-metadata": "0.2.2",
7475
"rxjs": "7.8.2",
7576
"sharp": "0.34.5",
7677
"uuid": "13.0.0",
@@ -81,7 +82,7 @@
8182
"@nestjs/cli": "11.0.21",
8283
"@nestjs/schematics": "11.1.0",
8384
"@nestjs/testing": "11.1.19",
84-
"@swc/cli": "0.7.9",
85+
"@swc/cli": "0.8.1",
8586
"@swc/core": "1.15.3",
8687
"@testcontainers/postgresql": "11.10.0",
8788
"@types/bcrypt": "6.0.0",
@@ -102,7 +103,7 @@
102103
"ts-jest": "29.4.5",
103104
"ts-node": "10.9.2",
104105
"tsconfig-paths": "4.2.0",
105-
"tsx": "4.20.6",
106+
"tsx": "4.22.4",
106107
"typescript": "5.9.3"
107108
},
108109
"engines": {

apps/backend-services/src/benchmark/dataset.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,14 +414,14 @@ export class DatasetService {
414414
_actorId: string, // TODO: Why isn't this used?
415415
groupId: string,
416416
): Promise<UploadResponseDto> {
417-
this.logger.log(
418-
`Uploading ${files.length} files to dataset ${datasetId}, version ${versionId}`,
419-
);
420-
421417
if (!Array.isArray(files)) {
422418
throw new BadRequestException("Invalid files payload");
423419
}
424420

421+
this.logger.log(
422+
`Uploading ${files.length} files to dataset ${datasetId}, version ${versionId}`,
423+
);
424+
425425
const dataset = await this.datasetDbService.findDataset(datasetId);
426426

427427
if (!dataset) {

apps/ches-adapter/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"@types/node": "22.15.17",
2828
"jest": "30.2.0",
2929
"ts-jest": "29.4.5",
30-
"tsx": "4.20.6",
30+
"tsx": "4.22.4",
3131
"typescript": "5.9.3"
3232
},
3333
"jest": {

apps/frontend/package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"@tanstack/react-query": "5.90.10",
3434
"@uiw/react-codemirror": "4.25.4",
3535
"@xyflow/react": "12.10.0",
36-
"axios": "1.15.0",
36+
"axios": "1.17.0",
3737
"dagre-esm": "0.8.5",
3838
"dayjs": "1.11.20",
3939
"konva": "10.2.0",
@@ -42,24 +42,25 @@
4242
"react": "19.2.1",
4343
"react-dom": "19.2.1",
4444
"react-konva": "19.2.1",
45-
"react-router-dom": "7.14.0",
45+
"react-router-dom": "7.17.0",
4646
"recharts": "3.7.0",
4747
"zod": "3.25.76"
4848
},
4949
"devDependencies": {
5050
"@biomejs/biome": "2.4.8",
51+
"@testing-library/dom": "10.4.1",
5152
"@testing-library/jest-dom": "6.9.1",
5253
"@testing-library/react": "16.3.2",
5354
"@types/dagre": "0.7.53",
5455
"@types/react": "19.2.7",
5556
"@types/react-dom": "19.2.3",
56-
"@vitejs/plugin-react": "5.1.2",
57-
"jsdom": "26.1.0",
57+
"@vitejs/plugin-react": "5.2.0",
58+
"jsdom": "29.1.1",
5859
"postcss": "8.5.6",
5960
"postcss-preset-mantine": "1.18.0",
6061
"postcss-simple-vars": "7.0.1",
6162
"typescript": "5.9.3",
62-
"vite": "7.3.2",
63-
"vitest": "4.0.18"
63+
"vite": "7.3.5",
64+
"vitest": "4.1.8"
6465
}
6566
}

apps/temporal/package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@
4141
"@temporalio/client": "1.10.0",
4242
"@temporalio/worker": "1.10.0",
4343
"@temporalio/workflow": "1.10.0",
44-
"axios": "1.15.0",
44+
"axios": "1.17.0",
4545
"csv": "6.5.1",
4646
"dictionary-en": "4.0.0",
4747
"dotenv": "17.2.3",
48-
"fast-xml-parser": "5.5.8",
48+
"fast-xml-builder": "1.2.0",
49+
"fast-xml-parser": "5.8.0",
50+
"fast-xml-validator": "1.1.0",
4951
"json-rules-engine": "4.0.0",
5052
"mupdf": "1.27.0",
5153
"nspell": "2.1.5",
@@ -56,7 +58,7 @@
5658
"devDependencies": {
5759
"@biomejs/biome": "2.4.8",
5860
"@temporalio/testing": "1.10.0",
59-
"@types/axios-mock-adapter": "1.9.0",
61+
"@types/axios-mock-adapter": "1.10.4",
6062
"@types/jest": "29.5.12",
6163
"@types/node": "20.10.0",
6264
"axios-mock-adapter": "2.1.0",
@@ -83,6 +85,8 @@
8385
"moduleNameMapper": {
8486
"^@generated/(.*)$": "<rootDir>/generated/$1"
8587
},
86-
"setupFilesAfterEnv": ["<rootDir>/jest-teardown.ts"]
88+
"setupFilesAfterEnv": [
89+
"<rootDir>/jest-teardown.ts"
90+
]
8791
}
8892
}

apps/temporal/src/activities/data-transform/execute.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ApplicationFailure } from "@temporalio/activity";
22
import { parse as parseCsv } from "csv/sync";
3-
import { XMLValidator } from "fast-xml-parser";
3+
import { SyntaxValidator } from "fast-xml-validator";
44
import { BindingResolutionError, resolveBindings } from "./binding-resolver";
55
import { renderCsv } from "./csv-renderer";
66
import type { InputFormat } from "./input-parser";
@@ -20,6 +20,7 @@ const KNOWN_PARAM_KEYS = new Set([
2020
"fieldMapping",
2121
"xmlEnvelope",
2222
"requestId",
23+
"groupId",
2324
]);
2425

2526
/**
@@ -45,6 +46,8 @@ export interface ExecuteTransformNodeParams {
4546
xmlEnvelope?: string;
4647
/** Optional request correlation ID (injected by the workflow runner). */
4748
requestId?: string;
49+
/** Tenant scope (injected by the workflow runner; not a port-binding input). */
50+
groupId?: string | number;
4851
/** Port-binding inputs from the workflow context (any additional keys). */
4952
[key: string]: unknown;
5053
}
@@ -131,21 +134,30 @@ export async function executeTransformNode(
131134

132135
// Step 4: Render the resolved mapping to the configured output format.
133136
let output: string;
134-
switch (outputFormat) {
135-
case "json":
136-
output = renderJson(resolvedMapping);
137-
break;
138-
case "xml": {
139-
const innerXml = renderXml(
140-
resolvedMapping,
141-
xmlEnvelope ? null : undefined,
142-
);
143-
output = injectXmlEnvelope(innerXml, xmlEnvelope);
144-
break;
137+
try {
138+
switch (outputFormat) {
139+
case "json":
140+
output = renderJson(resolvedMapping);
141+
break;
142+
case "xml": {
143+
const innerXml = renderXml(
144+
resolvedMapping,
145+
xmlEnvelope ? null : undefined,
146+
);
147+
output = injectXmlEnvelope(innerXml, xmlEnvelope);
148+
break;
149+
}
150+
case "csv":
151+
output = renderCsv(resolvedMapping);
152+
break;
145153
}
146-
case "csv":
147-
output = renderCsv(resolvedMapping);
148-
break;
154+
} catch (err) {
155+
const detail = err instanceof Error ? err.message : String(err);
156+
throw ApplicationFailure.create({
157+
type: "TRANSFORM_OUTPUT_ERROR",
158+
message: `Rendered ${outputFormat} output failed validation: ${detail}`,
159+
nonRetryable: true,
160+
});
149161
}
150162

151163
// Step 5: Post-render output validation.
@@ -159,9 +171,9 @@ export async function executeTransformNode(
159171
JSON.parse(output);
160172
break;
161173
case "xml": {
162-
const result = XMLValidator.validate(output);
174+
const result = SyntaxValidator.validate(output);
163175
if (result !== true) {
164-
throw new Error(result.err.msg);
176+
throw new Error((result as { err: { msg: string } }).err.msg);
165177
}
166178
break;
167179
}

apps/temporal/src/activities/data-transform/input-parser.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { parse as parseCsv } from "csv/sync";
2-
import { XMLParser, XMLValidator } from "fast-xml-parser";
2+
import { XMLParser } from "fast-xml-parser";
3+
import { SyntaxValidator } from "fast-xml-validator";
34

45
/** Supported input formats for the data transform node. */
56
export type InputFormat = "json" | "xml" | "csv";
@@ -78,9 +79,15 @@ function parseJson(input: string): Record<string, unknown> | unknown[] {
7879
* @throws {InputParseError} If the string is not valid XML.
7980
*/
8081
function parseXml(input: string): Record<string, unknown> {
81-
const validation = XMLValidator.validate(input);
82-
if (validation !== true) {
83-
throw new InputParseError("xml", validation.err.msg);
82+
try {
83+
const validation = SyntaxValidator.validate(input);
84+
if (validation !== true) {
85+
throw new InputParseError("xml", validation.err.msg);
86+
}
87+
} catch (err) {
88+
if (err instanceof InputParseError) throw err;
89+
const detail = err instanceof Error ? err.message : String(err);
90+
throw new InputParseError("xml", detail);
8491
}
8592

8693
const parser = new XMLParser({

apps/temporal/src/activities/data-transform/xml-renderer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { XMLBuilder } from "fast-xml-parser";
1+
import Builder from "fast-xml-builder";
22
import { IterationResult } from "./binding-resolver";
33

44
/** Regex pattern for valid XML element names. */
@@ -136,7 +136,7 @@ export function renderXml(
136136
validateElementNames(preprocessed);
137137

138138
try {
139-
const builder = new XMLBuilder({ format: false });
139+
const builder = new Builder({ format: false });
140140
return rootElement === null
141141
? builder.build(preprocessed)
142142
: builder.build({ [rootElement]: preprocessed });

apps/temporal/src/graph-engine/node-executors-transform.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,20 @@ describe("executeTransformNode activity — successful execution", () => {
153153

154154
expect(result.output).toBe(JSON.stringify({ name: "Alice" }));
155155
});
156+
157+
it("ignores system-injected groupId and does not attempt to parse it as input", async () => {
158+
// groupId is injected by the graph runner for tenant scoping and must
159+
// never be treated as a port-binding input, even for XML inputFormat.
160+
const params: ExecuteTransformNodeParams = {
161+
inputFormat: "xml",
162+
outputFormat: "json",
163+
fieldMapping: JSON.stringify({ orderId: "{{src.Order.OrderId}}" }),
164+
src: "<Order><OrderId>ORD-001</OrderId></Order>",
165+
groupId: "customer-group-123",
166+
};
167+
168+
const result = await executeTransformNode(params);
169+
170+
expect(result.output).toBe(JSON.stringify({ orderId: "ORD-001" }));
171+
});
156172
});

apps/temporal/src/graph-workflow.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ describe("Graph Workflow", () => {
275275

276276
expect(result.ctx.documentId).toBe("override-123");
277277
expect(result.status).toBe("completed");
278-
});
278+
}, 15000);
279279

280280
it("executes a linear 3-node graph (A -> B -> C)", async () => {
281281
const graph = makeLinearGraph();

0 commit comments

Comments
 (0)