From dcc574dc6536b30cb95582486544e7c210f57007 Mon Sep 17 00:00:00 2001 From: Krys Wisnaskas Date: Thu, 19 Sep 2024 09:59:51 -0400 Subject: [PATCH 01/22] Replace xml2json --- package.json | 4 +- src/lib/updateGrantsRecipients.js | 15 +++-- tests/api/README.md | 2 +- tests/api/activityReport.spec.ts | 2 +- tests/api/common.ts | 2 +- tests/api/externalapi.spec.ts | 2 +- tests/api/goals.spec.ts | 2 +- tests/api/recipient.spec.ts | 2 +- tests/api/role.spec.ts | 2 +- tests/api/settings.spec.ts | 2 +- tests/api/topics.spec.ts | 2 +- tests/api/user.spec.ts | 2 +- tests/api/users.spec.ts | 2 +- tests/api/widgets.spec.ts | 2 +- yarn.lock | 103 ++++++++++++++++-------------- 15 files changed, 77 insertions(+), 69 deletions(-) diff --git a/package.json b/package.json index d84ca2a6bf..21529bb5fe 100644 --- a/package.json +++ b/package.json @@ -265,7 +265,9 @@ "@babel/preset-env": "^7.11.5", "@babel/preset-typescript": "^7.18.6", "@cucumber/cucumber": "^7.0.0", + "@hapi/joi": "^17.1.1", "@playwright/test": "^1.28.0", + "@types/hapi__joi": "^17.1.14", "@types/jest": "^29.5.12", "@typescript-eslint/eslint-plugin": "^5.13.0", "@typescript-eslint/parser": "^5.0.0", @@ -284,7 +286,6 @@ "jest": "26.6.0", "jest-cli": "26.4.2", "jest-junit": "^12.0.0", - "joi": "^17.9.1", "nodemon": "^2.0.4", "playwright": "^1.46.0", "puppeteer": "^13.1.1", @@ -326,6 +327,7 @@ "express-http-context": "^1.2.4", "express-unless": "^1.0.0", "express-winston": "^4.0.5", + "fast-xml-parser": "^4.5.0", "file-type": "^16.5.4", "form-data": "^4.0.0", "he": "^1.2.0", diff --git a/src/lib/updateGrantsRecipients.js b/src/lib/updateGrantsRecipients.js index 254e882dd5..47bf514bc1 100644 --- a/src/lib/updateGrantsRecipients.js +++ b/src/lib/updateGrantsRecipients.js @@ -1,5 +1,5 @@ import AdmZip from 'adm-zip'; -import { toJson } from 'xml2json'; +import { XMLParser } from 'fast-xml-parser'; import axios from 'axios'; import { keyBy, mapValues } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; @@ -18,6 +18,8 @@ import { GRANT_PERSONNEL_ROLES } from '../constants'; const fs = require('mz/fs'); +const parser = new XMLParser(); + // TTAHUB-2126 TTAHUB-2334 // Update the specific attribute (e.g., state code) for each grant, // identified by its grant number, in the map below. @@ -215,8 +217,7 @@ export async function processFiles(hashSumHex) { ); const grantAgencyData = await fs.readFile('./temp/grant_agency.xml'); - const json = toJson(grantAgencyData); - const grantAgency = JSON.parse(json); + const grantAgency =parser.parse(grantAgencyData); // we are only interested in non-delegates const grantRecipients = grantAgency.grant_agencies.grant_agency.filter( (g) => g.grant_agency_number === '0', @@ -224,7 +225,7 @@ export async function processFiles(hashSumHex) { // process recipients aka agencies that are non-delegates const agencyData = await fs.readFile('./temp/agency.xml'); - const agency = JSON.parse(toJson(agencyData)); + const agency = parser.parse(agencyData); // filter out delegates by matching to the non-delegates; // filter out recipient 5 (TTAHUB-705) @@ -239,7 +240,7 @@ export async function processFiles(hashSumHex) { // process grants const grantData = await fs.readFile('./temp/grant_award.xml'); - const grant = JSON.parse(toJson(grantData)); + const grant = parser.parse(grantData); // temporary workaround for recipient 628 where it's name is coming in as DBA one. // This issue is pending with HSES as of 12/22/22. @@ -267,7 +268,7 @@ export async function processFiles(hashSumHex) { ); const programData = await fs.readFile('./temp/grant_program.xml'); - const programs = JSON.parse(toJson(programData)); + const programs = parser.parse(programData); const grantsForDb = grant.grant_awards.grant_award.map((g) => { let { @@ -373,7 +374,7 @@ export async function processFiles(hashSumHex) { // Load and Process grant replacement data. const grantReplacementsData = await fs.readFile('./temp/grant_award_replacement.xml'); - const grantReplacementsJson = JSON.parse(toJson(grantReplacementsData)); + const grantReplacementsJson = parser.parse(grantReplacementsData); const grantReplacements = grantReplacementsJson .grant_award_replacements.grant_award_replacement; diff --git a/tests/api/README.md b/tests/api/README.md index 0bde98c308..00c3a62376 100644 --- a/tests/api/README.md +++ b/tests/api/README.md @@ -10,7 +10,7 @@ There are a number of examples of writing tests in here, but the basic pattern i ```typescript import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test('get /endpoint', async ({ request }) => { diff --git a/tests/api/activityReport.spec.ts b/tests/api/activityReport.spec.ts index 8443916e2e..417f8b8a22 100644 --- a/tests/api/activityReport.spec.ts +++ b/tests/api/activityReport.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test.describe('get /activity-reports/goals', () => { diff --git a/tests/api/common.ts b/tests/api/common.ts index b04fa979c1..52ecda0eaa 100644 --- a/tests/api/common.ts +++ b/tests/api/common.ts @@ -1,5 +1,5 @@ import { APIResponse } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; export const root = `http://localhost:8080/api`; diff --git a/tests/api/externalapi.spec.ts b/tests/api/externalapi.spec.ts index 7ed4516a41..62283a3d30 100644 --- a/tests/api/externalapi.spec.ts +++ b/tests/api/externalapi.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test('get /v1/activity-reports/display/R01-AR-9999', async ({ request }) => { diff --git a/tests/api/goals.spec.ts b/tests/api/goals.spec.ts index 15df14ff11..9c3fbd56a0 100644 --- a/tests/api/goals.spec.ts +++ b/tests/api/goals.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from '@playwright/test'; import { CLOSE_SUSPEND_REASONS } from '@ttahub/common'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; import { GOAL_STATUS, OBJECTIVE_STATUS } from '../../src/constants'; diff --git a/tests/api/recipient.spec.ts b/tests/api/recipient.spec.ts index a1f577506b..5e82d07009 100644 --- a/tests/api/recipient.spec.ts +++ b/tests/api/recipient.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; enum SortBy { diff --git a/tests/api/role.spec.ts b/tests/api/role.spec.ts index 10d0ad33c9..c562d13c97 100644 --- a/tests/api/role.spec.ts +++ b/tests/api/role.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test.describe('get /role', () => { diff --git a/tests/api/settings.spec.ts b/tests/api/settings.spec.ts index ff8cd065d2..44687e4b1f 100644 --- a/tests/api/settings.spec.ts +++ b/tests/api/settings.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test('get /settings', async ({ request }) => { diff --git a/tests/api/topics.spec.ts b/tests/api/topics.spec.ts index 784630ee87..bfbf1eb13a 100644 --- a/tests/api/topics.spec.ts +++ b/tests/api/topics.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test('get /topic', async ({ request }) => { diff --git a/tests/api/user.spec.ts b/tests/api/user.spec.ts index 6879f95ba5..df9e5e2db3 100644 --- a/tests/api/user.spec.ts +++ b/tests/api/user.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test('get /user', async ({ request }) => { diff --git a/tests/api/users.spec.ts b/tests/api/users.spec.ts index 5bece90aa7..dae9478f87 100644 --- a/tests/api/users.spec.ts +++ b/tests/api/users.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { root, validateSchema } from './common'; test.describe('get /users/collaborators', () => { diff --git a/tests/api/widgets.spec.ts b/tests/api/widgets.spec.ts index 1b659e6415..493488166b 100644 --- a/tests/api/widgets.spec.ts +++ b/tests/api/widgets.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import Joi from 'joi'; +import Joi from '@hapi/joi'; import { reseed } from '../utils/common'; import { root, validateSchema } from './common'; diff --git a/yarn.lock b/yarn.lock index 1f24f6d7b8..150eb97a46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2001,6 +2001,13 @@ protobufjs "^7.2.4" yargs "^17.7.2" +"@hapi/address@^4.0.1": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-4.1.0.tgz#d60c5c0d930e77456fdcde2598e77302e2955e1d" + integrity sha512-SkszZf13HVgGmChdHo/PxchnSaCJ6cetVqLzyciudzZRT0jcOouIF/Q93mgjw8cce+D+4F4C1Z/WrfFN+O3VHQ== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/boom@^9.1.4": version "9.1.4" resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.4.tgz#1f9dad367c6a7da9f8def24b4a986fc5a7bd9db6" @@ -2008,11 +2015,32 @@ dependencies: "@hapi/hoek" "9.x.x" +"@hapi/formula@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-2.0.0.tgz#edade0619ed58c8e4f164f233cda70211e787128" + integrity sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A== + "@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== +"@hapi/joi@^17.1.1": + version "17.1.1" + resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-17.1.1.tgz#9cc8d7e2c2213d1e46708c6260184b447c661350" + integrity sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg== + dependencies: + "@hapi/address" "^4.0.1" + "@hapi/formula" "^2.0.0" + "@hapi/hoek" "^9.0.0" + "@hapi/pinpoint" "^2.0.0" + "@hapi/topo" "^5.0.0" + +"@hapi/pinpoint@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-2.0.1.tgz#32077e715655fc00ab8df74b6b416114287d6513" + integrity sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q== + "@hapi/topo@^5.0.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" @@ -2661,23 +2689,6 @@ resolved "https://registry.yarnpkg.com/@servie/events/-/events-1.0.0.tgz#8258684b52d418ab7b86533e861186638ecc5dc1" integrity sha512-sBSO19KzdrJCM3gdx6eIxV8M9Gxfgg6iDQmH5TIAGaUu+X9VDdsINXJOnoiZ1Kx3TrHdH4bt5UVglkjsEGBcvw== -"@sideway/address@^4.1.3": - version "4.1.4" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" - integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" - integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== - -"@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -3383,6 +3394,11 @@ dependencies: "@types/node" "*" +"@types/hapi__joi@^17.1.14": + version "17.1.14" + resolved "https://registry.yarnpkg.com/@types/hapi__joi/-/hapi__joi-17.1.14.tgz#937843b8a5d49fbfa8a87cc3527f6575e2140ca8" + integrity sha512-elV1VhwXUfA1sw59ij75HWyCH+3cA7xLbaOY9GQ+iQo/S+jSSf22LNZAmsXMdfV8DZwquCZaCT+F43Xf6/txrQ== + "@types/http-cache-semantics@*": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" @@ -6782,7 +6798,7 @@ fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== -fast-xml-parser@4.2.5, fast-xml-parser@^4.4.1: +fast-xml-parser@4.2.5, fast-xml-parser@^4.4.1, fast-xml-parser@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37" integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg== @@ -7380,9 +7396,9 @@ hoek@6.x.x: integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ== hoek@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" - integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== + version "4.3.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.3.1.tgz#1d2ea831857e4ecdce7fd5ffe07acdf8eb368cca" + integrity sha512-v7E+yIjcHECn973i0xHm4kJkEpv3C8sbYS4344WXbzYqRyiDD7rjnnKo4hsJkejQBAFdRMUGNHySeSPKSH9Rqw== hoist-non-react-statics@^3.0.0: version "3.3.2" @@ -8645,17 +8661,6 @@ joi@^13.1.2: isemail "3.x.x" topo "3.x.x" -joi@^17.9.1: - version "17.9.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.1.tgz#74899b9fa3646904afa984a11df648eca66c9018" - integrity sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw== - dependencies: - "@hapi/hoek" "^9.0.0" - "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.3" - "@sideway/formula" "^3.0.1" - "@sideway/pinpoint" "^2.0.0" - js-base64@^2.3.2: version "2.6.4" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -9569,16 +9574,16 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.13.2: - version "2.16.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== - nan@^2.17.0, nan@^2.18.0: version "2.18.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== +nan@^2.19.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" + integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== + nanoid@^3.2.0, nanoid@^3.3.6: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" @@ -9686,12 +9691,12 @@ node-environment-flags@^1.0.5: semver "^5.7.0" node-expat@^2.3.18: - version "2.4.0" - resolved "https://registry.yarnpkg.com/node-expat/-/node-expat-2.4.0.tgz#0d51626808b5912cf990b24b41244cfea14ee96b" - integrity sha512-X8Y/Zk/izfNgfayeOeUGqze7KlaOwVJ9SDTjHUMKd0hu0aFTRpLlLCBwmx79cTPiQWD24I1YOafF+U+rTvEMfQ== + version "2.4.1" + resolved "https://registry.yarnpkg.com/node-expat/-/node-expat-2.4.1.tgz#1e0848084d46457195967766c29be0cdbb6e2698" + integrity sha512-uWgvQLgo883NKIL+66oJsK9ysKK3ej0YjVCPBZzO/7wMAuH68/Yb7+JwPWNaVq0yPaxrb48AoEXfYEc8gsmFbg== dependencies: bindings "^1.5.0" - nan "^2.13.2" + nan "^2.19.0" node-fetch-h2@^2.3.0: version "2.3.0" @@ -10826,20 +10831,20 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +punycode@2.x.x, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== -punycode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== pupa@^2.1.1: version "2.1.1" From ef8c1666be5b02e594691f7ce753303f7eb117b4 Mon Sep 17 00:00:00 2001 From: Krys Wisnaskas Date: Thu, 19 Sep 2024 10:23:35 -0400 Subject: [PATCH 02/22] Fix lint error --- src/lib/updateGrantsRecipients.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/updateGrantsRecipients.js b/src/lib/updateGrantsRecipients.js index 47bf514bc1..5e915f3508 100644 --- a/src/lib/updateGrantsRecipients.js +++ b/src/lib/updateGrantsRecipients.js @@ -217,7 +217,7 @@ export async function processFiles(hashSumHex) { ); const grantAgencyData = await fs.readFile('./temp/grant_agency.xml'); - const grantAgency =parser.parse(grantAgencyData); + const grantAgency = parser.parse(grantAgencyData); // we are only interested in non-delegates const grantRecipients = grantAgency.grant_agencies.grant_agency.filter( (g) => g.grant_agency_number === '0', From 55ebfcd985d3ae531fe66c10b20ff1544b5727be Mon Sep 17 00:00:00 2001 From: Krys Wisnaskas Date: Thu, 19 Sep 2024 10:58:57 -0400 Subject: [PATCH 03/22] Update known vulnerabilities --- yarn-audit-known-issues | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn-audit-known-issues b/yarn-audit-known-issues index 49bae093ea..752a6baacc 100644 --- a/yarn-audit-known-issues +++ b/yarn-audit-known-issues @@ -1,4 +1,4 @@ -{"type":"auditAdvisory","data":{"resolution":{"id":1096366,"path":"email-templates>preview-email>mailparser>nodemailer","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"6.7.3","paths":["email-templates>preview-email>mailparser>nodemailer"]}],"metadata":null,"vulnerable_versions":"<=6.9.8","module_name":"nodemailer","severity":"moderate","github_advisory_id":"GHSA-9h6g-pr28-7cqp","cves":[],"access":"public","patched_versions":">=6.9.9","cvss":{"score":5.3,"vectorString":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L"},"updated":"2024-02-01T17:58:50.000Z","recommendation":"Upgrade to version 6.9.9 or later","cwe":["CWE-1333"],"found_by":null,"deleted":null,"id":1096366,"references":"- https://github.com/nodemailer/nodemailer/security/advisories/GHSA-9h6g-pr28-7cqp\n- https://gist.github.com/francoatmega/890dd5053375333e40c6fdbcc8c58df6\n- https://gist.github.com/francoatmega/9aab042b0b24968d7b7039818e8b2698\n- https://github.com/nodemailer/nodemailer/commit/dd8f5e8a4ddc99992e31df76bcff9c590035cd4a\n- https://github.com/advisories/GHSA-9h6g-pr28-7cqp","created":"2024-01-31T22:42:54.000Z","reported_by":null,"title":"nodemailer ReDoS when trying to send a specially crafted email","npm_advisory_id":null,"overview":"### Summary\nA ReDoS vulnerability occurs when nodemailer tries to parse img files with the parameter `attachDataUrls` set, causing the stuck of event loop. \nAnother flaw was found when nodemailer tries to parse an attachments with a embedded file, causing the stuck of event loop. \n\n### Details\n\nRegex: /^data:((?:[^;]*;)*(?:[^,]*)),(.*)$/\n\nPath: compile -> getAttachments -> _processDataUrl\n\nRegex: /(]* src\\s*=[\\s\"']*)(data:([^;]+);[^\"'>\\s]+)/\n\nPath: _convertDataImages\n\n### PoC\n\nhttps://gist.github.com/francoatmega/890dd5053375333e40c6fdbcc8c58df6\nhttps://gist.github.com/francoatmega/9aab042b0b24968d7b7039818e8b2698\n\n### Impact\n\nReDoS causes the event loop to stuck a specially crafted evil email can cause this problem.\n","url":"https://github.com/advisories/GHSA-9h6g-pr28-7cqp"}}} -{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.2.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"metadata":null,"vulnerable_versions":"<=6.1.3","module_name":"hoek","severity":"high","github_advisory_id":"GHSA-c429-5p7v-vgjp","cves":["CVE-2020-36604"],"access":"public","patched_versions":"<0.0.0","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"updated":"2024-02-07T18:59:37.000Z","recommendation":"None","cwe":["CWE-1321"],"found_by":null,"deleted":null,"id":1096410,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} -{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>joi>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.2.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"metadata":null,"vulnerable_versions":"<=6.1.3","module_name":"hoek","severity":"high","github_advisory_id":"GHSA-c429-5p7v-vgjp","cves":["CVE-2020-36604"],"access":"public","patched_versions":"<0.0.0","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"updated":"2024-02-07T18:59:37.000Z","recommendation":"None","cwe":["CWE-1321"],"found_by":null,"deleted":null,"id":1096410,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} -{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>joi>topo>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.2.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"metadata":null,"vulnerable_versions":"<=6.1.3","module_name":"hoek","severity":"high","github_advisory_id":"GHSA-c429-5p7v-vgjp","cves":["CVE-2020-36604"],"access":"public","patched_versions":"<0.0.0","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"updated":"2024-02-07T18:59:37.000Z","recommendation":"None","cwe":["CWE-1321"],"found_by":null,"deleted":null,"id":1096410,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} +{"type":"auditAdvisory","data":{"resolution":{"id":1096366,"path":"email-templates>preview-email>mailparser>nodemailer","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"6.7.3","paths":["email-templates>preview-email>mailparser>nodemailer"]}],"found_by":null,"deleted":null,"references":"- https://github.com/nodemailer/nodemailer/security/advisories/GHSA-9h6g-pr28-7cqp\n- https://gist.github.com/francoatmega/890dd5053375333e40c6fdbcc8c58df6\n- https://gist.github.com/francoatmega/9aab042b0b24968d7b7039818e8b2698\n- https://github.com/nodemailer/nodemailer/commit/dd8f5e8a4ddc99992e31df76bcff9c590035cd4a\n- https://github.com/advisories/GHSA-9h6g-pr28-7cqp","created":"2024-01-31T22:42:54.000Z","id":1096366,"npm_advisory_id":null,"overview":"### Summary\nA ReDoS vulnerability occurs when nodemailer tries to parse img files with the parameter `attachDataUrls` set, causing the stuck of event loop. \nAnother flaw was found when nodemailer tries to parse an attachments with a embedded file, causing the stuck of event loop. \n\n### Details\n\nRegex: /^data:((?:[^;]*;)*(?:[^,]*)),(.*)$/\n\nPath: compile -> getAttachments -> _processDataUrl\n\nRegex: /(]* src\\s*=[\\s\"']*)(data:([^;]+);[^\"'>\\s]+)/\n\nPath: _convertDataImages\n\n### PoC\n\nhttps://gist.github.com/francoatmega/890dd5053375333e40c6fdbcc8c58df6\nhttps://gist.github.com/francoatmega/9aab042b0b24968d7b7039818e8b2698\n\n### Impact\n\nReDoS causes the event loop to stuck a specially crafted evil email can cause this problem.\n","reported_by":null,"title":"nodemailer ReDoS when trying to send a specially crafted email","metadata":null,"cves":[],"access":"public","severity":"moderate","module_name":"nodemailer","vulnerable_versions":"<=6.9.8","github_advisory_id":"GHSA-9h6g-pr28-7cqp","recommendation":"Upgrade to version 6.9.9 or later","patched_versions":">=6.9.9","updated":"2024-02-01T17:58:50.000Z","cvss":{"score":5.3,"vectorString":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L"},"cwe":["CWE-1333"],"url":"https://github.com/advisories/GHSA-9h6g-pr28-7cqp"}}} +{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.3.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","id":1096410,"npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","metadata":null,"cves":["CVE-2020-36604"],"access":"public","severity":"high","module_name":"hoek","vulnerable_versions":"<=6.1.3","github_advisory_id":"GHSA-c429-5p7v-vgjp","recommendation":"None","patched_versions":"<0.0.0","updated":"2024-02-07T18:59:37.000Z","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} +{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>joi>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.3.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","id":1096410,"npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","metadata":null,"cves":["CVE-2020-36604"],"access":"public","severity":"high","module_name":"hoek","vulnerable_versions":"<=6.1.3","github_advisory_id":"GHSA-c429-5p7v-vgjp","recommendation":"None","patched_versions":"<0.0.0","updated":"2024-02-07T18:59:37.000Z","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} +{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>joi>topo>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.3.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","id":1096410,"npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","metadata":null,"cves":["CVE-2020-36604"],"access":"public","severity":"high","module_name":"hoek","vulnerable_versions":"<=6.1.3","github_advisory_id":"GHSA-c429-5p7v-vgjp","recommendation":"None","patched_versions":"<0.0.0","updated":"2024-02-07T18:59:37.000Z","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} From 1996236a635a1581defff828b2c965f9a5b338d5 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Thu, 19 Sep 2024 11:28:49 -0400 Subject: [PATCH 04/22] updates to event view --- .../ViewTrainingReport/__tests__/index.js | 85 +++++++++++++++++++ .../src/pages/ViewTrainingReport/index.js | 49 ++++++++--- 2 files changed, 120 insertions(+), 14 deletions(-) diff --git a/frontend/src/pages/ViewTrainingReport/__tests__/index.js b/frontend/src/pages/ViewTrainingReport/__tests__/index.js index 2a6e603641..ade7976cb0 100644 --- a/frontend/src/pages/ViewTrainingReport/__tests__/index.js +++ b/frontend/src/pages/ViewTrainingReport/__tests__/index.js @@ -581,6 +581,91 @@ describe('ViewTrainingReport', () => { expect(await screen.findByText('USER 2')).toBeInTheDocument(); }); + it('displays the is ist visit field and the appropriate participants', async () => { + const e = mockEvent(); + e.sessionReports = [{ + ...e.sessionReports[0], + data: { + ...e.sessionReports[0].data, + isIstVisit: 'yes', + regionalOfficeTta: ['Ist Office 1', 'Ist Office 2'], + }, + }, + { + ...e.sessionReports[1], + data: { + ...e.sessionReports[1].data, + isIstVisit: 'no', + recipients: [{ label: 'Recipient 1' }, { label: 'Recipient 2' }], + participants: ['Participants 1', 'Participants 2'], + }, + }]; + fetchMock.getOnce('/api/events/id/1?readOnly=true', e); + + fetchMock.getOnce('/api/users/names?ids=1', ['USER 1']); + fetchMock.getOnce('/api/users/names?ids=2', ['USER 2']); + + act(() => { + renderTrainingReport(); + }); + + expect(await screen.findByRole('heading', { name: 'Training event report R03-PD-23-1037' })).toBeInTheDocument(); + + expect(screen.queryAllByText('IST visit').length).toBe(2); + expect(await screen.findByText('Regional Office/TTA')).toBeInTheDocument(); + expect(await screen.findByText('Yes')).toBeInTheDocument(); + expect(await screen.findByText(/ist office 1, ist office 2/i)).toBeInTheDocument(); + + expect(await screen.findByText('Recipient participants')).toBeInTheDocument(); + expect(await screen.findByText('No')).toBeInTheDocument(); + expect(await screen.findByText(/Recipient 1, Recipient 2/i)).toBeInTheDocument(); + + expect(await screen.findByText('Recipient participants')).toBeInTheDocument(); + expect(await screen.findByText(/Participants 1, Participants 2/i)).toBeInTheDocument(); + }); + + it('displays the delivery method field and the appropriate participants attending', async () => { + const e = mockEvent(); + e.sessionReports = [{ + ...e.sessionReports[0], + data: { + ...e.sessionReports[0].data, + deliveryMethod: 'in-person', + numberOfParticipants: 10, + }, + }, + { + ...e.sessionReports[1], + data: { + ...e.sessionReports[1].data, + deliveryMethod: 'hybrid', + numberOfParticipantsInPerson: 11, + numberOfParticipantsVirtually: 12, + }, + }]; + fetchMock.getOnce('/api/events/id/1?readOnly=true', e); + + fetchMock.getOnce('/api/users/names?ids=1', ['USER 1']); + fetchMock.getOnce('/api/users/names?ids=2', ['USER 2']); + + act(() => { + renderTrainingReport(); + }); + + expect(await screen.findByRole('heading', { name: 'Training event report R03-PD-23-1037' })).toBeInTheDocument(); + + expect(screen.queryAllByText('Delivery method').length).toBe(2); + expect(await screen.findByText('In-person')).toBeInTheDocument(); + expect(await screen.findByText('Number of participants')).toBeInTheDocument(); + expect(await screen.findByText('10')).toBeInTheDocument(); + + expect(await screen.findByText('Hybrid')).toBeInTheDocument(); + expect(await screen.findByText('Number of participants attending in person')).toBeInTheDocument(); + expect(await screen.findByText('11')).toBeInTheDocument(); + expect(await screen.findByText('Number of participants attending virtually')).toBeInTheDocument(); + expect(await screen.findByText('12')).toBeInTheDocument(); + }); + describe('formatOwnerName', () => { test('handles an error', () => { const result = formatOwnerName({ eventReportPilotNationalCenterUsers: 123 }); diff --git a/frontend/src/pages/ViewTrainingReport/index.js b/frontend/src/pages/ViewTrainingReport/index.js index acb9d9d60f..81648272ea 100644 --- a/frontend/src/pages/ViewTrainingReport/index.js +++ b/frontend/src/pages/ViewTrainingReport/index.js @@ -214,14 +214,41 @@ export default function ViewTrainingReport({ match }) { 'Training type': event.data['Event Duration/# NC Days of Support'], Reasons: event.data.reasons, 'Target populations': event.data.targetPopulations, - }, - striped: true, - }, { - heading: 'Vision', - data: { Vision: event.data.vision, }, + striped: true, }] : []; + // console.log('sessions: ', event.sessionReports); + + const generateIstOfficeOrRecipientProperties = (session) => { + if (session.data.isIstVisit === 'yes') { + return { + 'Regional Office/TTA': session.data.regionalOfficeTta.join(', '), + }; + } + + return { + Recipients: session.data.recipients ? session.data.recipients.map((r) => r.label).join(', ') : '', + 'Recipient participants': session.data.participants ? session.data.participants.join(', ') : [], + }; + }; + + const generateNumberOfParticipants = (session) => { + // In person or virtual. + if (session.data.deliveryMethod === 'in-person' || session.data.deliveryMethod === 'virtual') { + const numberOfParticipants = session.data.numberOfParticipants ? session.data.numberOfParticipants.toString() : ''; + return { + 'Number of participants': numberOfParticipants, + }; + } + // Hybrid. + const numberOfParticipantsInPerson = session.data.numberOfParticipantsInPerson ? session.data.numberOfParticipantsInPerson.toString() : ''; + const numberOfParticipantsVirtually = session.data.numberOfParticipantsVirtually ? session.data.numberOfParticipantsVirtually.toString() : ''; + return { + 'Number of participants attending in person': numberOfParticipantsInPerson, + 'Number of participants attending virtually': numberOfParticipantsVirtually, + }; + }; const sessions = event && event.sessionReports ? event.sessionReports.map((session, index) => ( r.label).join(', ') : '', - 'Recipient participants': session.data.participants ? session.data.participants.join(', ') : [], - 'Number of participants': String(( - session.data.numberOfParticipants || 0 - ) + ( - session.data.numberOfParticipantsVirtually || 0 - ) + ( - session.data.numberOfParticipantsInPerson || 0 - )), + 'IST visit': session.data.isIstVisit ? capitalize(session.data.isIstVisit) : '', + ...generateIstOfficeOrRecipientProperties(session), 'Delivery method': capitalize(session.data.deliveryMethod || ''), + ...generateNumberOfParticipants(session), 'Language used': session.data.language ? session.data.language.join(', ') : [], 'TTA provided': session.data.ttaProvided, }, From a3322bcb2b4725715e3116d35d687db601f4e49b Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 00:15:57 -0700 Subject: [PATCH 05/22] Add a new hook to update the flags as part of the merge process when the originalGoalId is updated --- src/models/hooks/activityReportGoal.js | 42 +++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/models/hooks/activityReportGoal.js b/src/models/hooks/activityReportGoal.js index 5b22f0fe62..2ef2018006 100644 --- a/src/models/hooks/activityReportGoal.js +++ b/src/models/hooks/activityReportGoal.js @@ -152,6 +152,44 @@ const destroyLinkedSimilarityGroups = async (sequelize, instance, options) => { }); }; +const updateOnARAndOnApprovedARForMergedGoals = async (sequelize, instance) => { + const changed = instance.changed(); + + // Check if both originalGoalId and goalId have been changed and originalGoalId is not null + if (Array.isArray(changed) + && changed.includes('originalGoalId') + && changed.includes('goalId') + && instance.originalGoalId !== null) { + const goalId = instance.goalId; + + // Check if the ActivityReport linked to this ActivityReportGoal has a calculatedStatus of 'approved' + const approvedActivityReports = await sequelize.models.ActivityReport.count({ + where: { + calculatedStatus: 'approved', + id: instance.activityReportId, // Use the activityReportId from the instance + }, + }); + + const onApprovedAR = approvedActivityReports > 0; + + // Update only if the current values differ + await sequelize.models.Goal.update( + { onAR: true, onApprovedAR }, + { + where: { + id: goalId, + [sequelize.Op.or]: [ + { onAR: { [sequelize.Op.ne]: true } }, // Update if onAR is not already true + { onApprovedAR: { [sequelize.Op.ne]: onApprovedAR } }, // Update if onApprovedAR differs + ], + }, + individualHooks: true, // Ensure individual hooks are triggered + } + ); + } +}; + + const afterCreate = async (sequelize, instance, options) => { await processForEmbeddedResources(sequelize, instance, options); await autoPopulateLinker(sequelize, instance, options); @@ -176,7 +214,8 @@ const afterDestroy = async (sequelize, instance, options) => { const afterUpdate = async (sequelize, instance, options) => { await processForEmbeddedResources(sequelize, instance, options); - await destroyLinkedSimilarityGroups(sequelize, instance, options); + await destroyLinkedSimilarityGroups(sequelize, instance, options); + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance); }; export { @@ -185,6 +224,7 @@ export { recalculateOnAR, propagateDestroyToMetadata, destroyLinkedSimilarityGroups, + updateOnARAndOnApprovedARForMergedGoals, afterCreate, beforeDestroy, afterDestroy, From 5d091827ca868498f32cd90ee28f14291cd2c5b4 Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 00:16:12 -0700 Subject: [PATCH 06/22] Tests to cover the new hook --- src/models/hooks/activityReportGoal.test.js | 165 +++++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/src/models/hooks/activityReportGoal.test.js b/src/models/hooks/activityReportGoal.test.js index 882bf46506..ffa9a7c548 100644 --- a/src/models/hooks/activityReportGoal.test.js +++ b/src/models/hooks/activityReportGoal.test.js @@ -1,5 +1,8 @@ const { REPORT_STATUSES } = require('@ttahub/common'); -const { destroyLinkedSimilarityGroups } = require('./activityReportGoal'); +const { + destroyLinkedSimilarityGroups, + updateOnARAndOnApprovedARForMergedGoals, +} = require('./activityReportGoal'); describe('destroyLinkedSimilarityGroups', () => { afterEach(() => { @@ -205,3 +208,163 @@ describe('destroyLinkedSimilarityGroups', () => { expect(sequelize.models.GoalSimilarityGroup.destroy).not.toHaveBeenCalled(); }); }); + +describe('updateOnARAndOnApprovedARForMergedGoals', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + const sequelize = { + models: { + Goal: { + update: jest.fn(), + }, + ActivityReport: { + count: jest.fn(), + }, + }, + }; + + it('should update onAR and onApprovedAR for merged goals when originalGoalId and goalId are changed', async () => { + const instance = { + goalId: 1, + originalGoalId: 2, + activityReportId: 1, + changed: () => ['originalGoalId', 'goalId'], // Simulate that both columns have changed + }; + + const options = { + transaction: 'mockTransaction', + }; + + // Mock the necessary Sequelize methods + sequelize.models.ActivityReport.count.mockResolvedValue(1); // Simulate approved ActivityReport exists + + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance, options); + + expect(sequelize.models.ActivityReport.count).toHaveBeenCalledWith({ + where: { + calculatedStatus: 'approved', + id: instance.activityReportId, + }, + }); + + expect(sequelize.models.Goal.update).toHaveBeenCalledWith( + { onAR: true, onApprovedAR: true }, + { + where: { + id: instance.goalId, + [sequelize.Op.or]: [ + { onAR: { [sequelize.Op.ne]: true } }, // Ensure onAR condition is in the where clause + { onApprovedAR: { [sequelize.Op.ne]: true } }, // Ensure onApprovedAR condition is in the where clause + ], + }, + individualHooks: true, + } + ); + }); + + + it('should update onAR and onApprovedAR with false when there are no approved activity reports', async () => { + const instance = { + goalId: 1, + originalGoalId: 2, + activityReportId: 1, + changed: () => ['originalGoalId', 'goalId'], // Simulate that both columns have changed + }; + + const options = { + transaction: 'mockTransaction', + }; + + // Mock the necessary Sequelize methods + sequelize.models.ActivityReport.count.mockResolvedValue(0); // No approved ActivityReports + + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance, options); + + expect(sequelize.models.ActivityReport.count).toHaveBeenCalledWith({ + where: { + calculatedStatus: 'approved', + id: instance.activityReportId, + }, + }); + + expect(sequelize.models.Goal.update).toHaveBeenCalledWith( + { onAR: true, onApprovedAR: false }, // onApprovedAR is false since no approved reports + { + where: { id: instance.goalId }, + individualHooks: true, + } + ); + }); + + it('should not update if originalGoalId or goalId is not changed', async () => { + const instance = { + goalId: 1, + originalGoalId: 2, + activityReportId: 1, + changed: () => [], // Simulate no changes + }; + + const options = { + transaction: 'mockTransaction', + }; + + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance, options); + + expect(sequelize.models.ActivityReport.count).not.toHaveBeenCalled(); + expect(sequelize.models.Goal.update).not.toHaveBeenCalled(); + }); + + it('should not update if originalGoalId is null', async () => { + const instance = { + goalId: 1, + originalGoalId: null, + activityReportId: 1, + changed: () => ['originalGoalId', 'goalId'], // Simulate that both columns have changed + }; + + const options = { + transaction: 'mockTransaction', + }; + + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance, options); + + expect(sequelize.models.ActivityReport.count).not.toHaveBeenCalled(); + expect(sequelize.models.Goal.update).not.toHaveBeenCalled(); + }); + + it('should not update if onAR and onApprovedAR are already set to the correct values', async () => { + const instance = { + goalId: 1, + originalGoalId: 2, + activityReportId: 1, + changed: () => ['originalGoalId', 'goalId'], // Simulate that both columns have changed + }; + + const options = { + transaction: 'mockTransaction', + }; + + // Mock the necessary Sequelize methods + sequelize.models.ActivityReport.count.mockResolvedValue(1); // Simulate approved ActivityReport exists + sequelize.models.Goal.update.mockResolvedValue(0); // Simulate that the update doesn't happen + + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance, options); + + expect(sequelize.models.Goal.update).toHaveBeenCalledWith( + { onAR: true, onApprovedAR: true }, + { + where: { + id: instance.goalId, + [sequelize.Op.or]: [ + { onAR: { [sequelize.Op.ne]: true } }, // Check if onAR is already true + { onApprovedAR: { [sequelize.Op.ne]: true } }, // Check if onApprovedAR is already true + ], + }, + individualHooks: true, + } + ); + }); + +}); From 719d59e69808dedb83d3dd46a1c31bbf43b6ac71 Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 00:33:32 -0700 Subject: [PATCH 07/22] linter fixes --- src/models/hooks/activityReportGoal.js | 20 +++++----- src/models/hooks/activityReportGoal.test.js | 43 +++++++++++---------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/models/hooks/activityReportGoal.js b/src/models/hooks/activityReportGoal.js index 2ef2018006..e39b986ce8 100644 --- a/src/models/hooks/activityReportGoal.js +++ b/src/models/hooks/activityReportGoal.js @@ -160,13 +160,14 @@ const updateOnARAndOnApprovedARForMergedGoals = async (sequelize, instance) => { && changed.includes('originalGoalId') && changed.includes('goalId') && instance.originalGoalId !== null) { - const goalId = instance.goalId; + const {goalId} = instance; - // Check if the ActivityReport linked to this ActivityReportGoal has a calculatedStatus of 'approved' + // Check if the ActivityReport linked to this ActivityReportGoal has a + // calculatedStatus of 'approved' const approvedActivityReports = await sequelize.models.ActivityReport.count({ where: { calculatedStatus: 'approved', - id: instance.activityReportId, // Use the activityReportId from the instance + id: instance.activityReportId, // Use the activityReportId from the instance }, }); @@ -179,17 +180,18 @@ const updateOnARAndOnApprovedARForMergedGoals = async (sequelize, instance) => { where: { id: goalId, [sequelize.Op.or]: [ - { onAR: { [sequelize.Op.ne]: true } }, // Update if onAR is not already true - { onApprovedAR: { [sequelize.Op.ne]: onApprovedAR } }, // Update if onApprovedAR differs + // Update if onAR is not already true + { onAR: { [sequelize.Op.ne]: true } }, + // Update if onApprovedAR differs + { onApprovedAR: { [sequelize.Op.ne]: onApprovedAR } }, ], }, - individualHooks: true, // Ensure individual hooks are triggered - } + individualHooks: true, // Ensure individual hooks are triggered + }, ); } }; - const afterCreate = async (sequelize, instance, options) => { await processForEmbeddedResources(sequelize, instance, options); await autoPopulateLinker(sequelize, instance, options); @@ -214,7 +216,7 @@ const afterDestroy = async (sequelize, instance, options) => { const afterUpdate = async (sequelize, instance, options) => { await processForEmbeddedResources(sequelize, instance, options); - await destroyLinkedSimilarityGroups(sequelize, instance, options); + await destroyLinkedSimilarityGroups(sequelize, instance, options); await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance); }; diff --git a/src/models/hooks/activityReportGoal.test.js b/src/models/hooks/activityReportGoal.test.js index ffa9a7c548..0553cc4839 100644 --- a/src/models/hooks/activityReportGoal.test.js +++ b/src/models/hooks/activityReportGoal.test.js @@ -232,38 +232,40 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { activityReportId: 1, changed: () => ['originalGoalId', 'goalId'], // Simulate that both columns have changed }; - + const options = { transaction: 'mockTransaction', }; - + // Mock the necessary Sequelize methods - sequelize.models.ActivityReport.count.mockResolvedValue(1); // Simulate approved ActivityReport exists - + // Simulate approved ActivityReport exists + sequelize.models.ActivityReport.count.mockResolvedValue(1); + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance, options); - + expect(sequelize.models.ActivityReport.count).toHaveBeenCalledWith({ where: { calculatedStatus: 'approved', id: instance.activityReportId, }, }); - + expect(sequelize.models.Goal.update).toHaveBeenCalledWith( { onAR: true, onApprovedAR: true }, { where: { id: instance.goalId, [sequelize.Op.or]: [ - { onAR: { [sequelize.Op.ne]: true } }, // Ensure onAR condition is in the where clause - { onApprovedAR: { [sequelize.Op.ne]: true } }, // Ensure onApprovedAR condition is in the where clause + // Ensure onAR condition is in the where clause + { onAR: { [sequelize.Op.ne]: true } }, + // Ensure onApprovedAR condition is in the where clause + { onApprovedAR: { [sequelize.Op.ne]: true } }, ], }, individualHooks: true, - } + }, ); }); - it('should update onAR and onApprovedAR with false when there are no approved activity reports', async () => { const instance = { @@ -294,7 +296,7 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { { where: { id: instance.goalId }, individualHooks: true, - } + }, ); }); @@ -341,17 +343,19 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { activityReportId: 1, changed: () => ['originalGoalId', 'goalId'], // Simulate that both columns have changed }; - + const options = { transaction: 'mockTransaction', }; - + // Mock the necessary Sequelize methods - sequelize.models.ActivityReport.count.mockResolvedValue(1); // Simulate approved ActivityReport exists - sequelize.models.Goal.update.mockResolvedValue(0); // Simulate that the update doesn't happen - + // Simulate approved ActivityReport exists + sequelize.models.ActivityReport.count.mockResolvedValue(1); + // Simulate that the update doesn't happen + sequelize.models.Goal.update.mockResolvedValue(0); + await updateOnARAndOnApprovedARForMergedGoals(sequelize, instance, options); - + expect(sequelize.models.Goal.update).toHaveBeenCalledWith( { onAR: true, onApprovedAR: true }, { @@ -363,8 +367,7 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { ], }, individualHooks: true, - } + }, ); - }); - + }); }); From 3954d9ee68e9d6ebcf63880c2ce055172f06c755 Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 00:38:25 -0700 Subject: [PATCH 08/22] more lint --- src/models/hooks/activityReportGoal.js | 2 +- src/models/hooks/activityReportGoal.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/hooks/activityReportGoal.js b/src/models/hooks/activityReportGoal.js index e39b986ce8..ea40ec80ed 100644 --- a/src/models/hooks/activityReportGoal.js +++ b/src/models/hooks/activityReportGoal.js @@ -160,7 +160,7 @@ const updateOnARAndOnApprovedARForMergedGoals = async (sequelize, instance) => { && changed.includes('originalGoalId') && changed.includes('goalId') && instance.originalGoalId !== null) { - const {goalId} = instance; + const { goalId } = instance; // Check if the ActivityReport linked to this ActivityReportGoal has a // calculatedStatus of 'approved' diff --git a/src/models/hooks/activityReportGoal.test.js b/src/models/hooks/activityReportGoal.test.js index 0553cc4839..1027fc84d7 100644 --- a/src/models/hooks/activityReportGoal.test.js +++ b/src/models/hooks/activityReportGoal.test.js @@ -369,5 +369,5 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { individualHooks: true, }, ); - }); + }); }); From 66e268d4f60397741981e9b45862c06c3028309d Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 01:17:53 -0700 Subject: [PATCH 09/22] Update activityReportGoal.test.js --- src/models/hooks/activityReportGoal.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/models/hooks/activityReportGoal.test.js b/src/models/hooks/activityReportGoal.test.js index 1027fc84d7..a72ae48330 100644 --- a/src/models/hooks/activityReportGoal.test.js +++ b/src/models/hooks/activityReportGoal.test.js @@ -1,3 +1,4 @@ +const { Op } = require('sequelize'); // Import Sequelize operators const { REPORT_STATUSES } = require('@ttahub/common'); const { destroyLinkedSimilarityGroups, From 4da4e9f047eb5b777f29f2e250e79cc7646637a4 Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 08:57:22 -0700 Subject: [PATCH 10/22] Fix implementation error --- src/models/hooks/activityReportGoal.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/models/hooks/activityReportGoal.js b/src/models/hooks/activityReportGoal.js index ea40ec80ed..1552e1402f 100644 --- a/src/models/hooks/activityReportGoal.js +++ b/src/models/hooks/activityReportGoal.js @@ -1,3 +1,4 @@ +const { Op } = require('sequelize'); const { REPORT_STATUSES } = require('@ttahub/common'); const { GOAL_COLLABORATORS } = require('../../constants'); const { @@ -179,11 +180,11 @@ const updateOnARAndOnApprovedARForMergedGoals = async (sequelize, instance) => { { where: { id: goalId, - [sequelize.Op.or]: [ + [Op.or]: [ // Update if onAR is not already true - { onAR: { [sequelize.Op.ne]: true } }, + { onAR: { [Op.ne]: true } }, // Update if onApprovedAR differs - { onApprovedAR: { [sequelize.Op.ne]: onApprovedAR } }, + { onApprovedAR: { [Op.ne]: onApprovedAR } }, ], }, individualHooks: true, // Ensure individual hooks are triggered From df0f71dec16f96f3e0a1e35dcd3cff9663f3a63e Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 09:07:24 -0700 Subject: [PATCH 11/22] Update activityReportGoal.test.js --- src/models/hooks/activityReportGoal.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/models/hooks/activityReportGoal.test.js b/src/models/hooks/activityReportGoal.test.js index a72ae48330..1d418205b6 100644 --- a/src/models/hooks/activityReportGoal.test.js +++ b/src/models/hooks/activityReportGoal.test.js @@ -256,11 +256,11 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { { where: { id: instance.goalId, - [sequelize.Op.or]: [ + [Op.or]: [ // Ensure onAR condition is in the where clause - { onAR: { [sequelize.Op.ne]: true } }, + { onAR: { [Op.ne]: true } }, // Ensure onApprovedAR condition is in the where clause - { onApprovedAR: { [sequelize.Op.ne]: true } }, + { onApprovedAR: { [Op.ne]: true } }, ], }, individualHooks: true, @@ -362,9 +362,9 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { { where: { id: instance.goalId, - [sequelize.Op.or]: [ - { onAR: { [sequelize.Op.ne]: true } }, // Check if onAR is already true - { onApprovedAR: { [sequelize.Op.ne]: true } }, // Check if onApprovedAR is already true + [Op.or]: [ + { onAR: { [Op.ne]: true } }, // Check if onAR is already true + { onApprovedAR: { [Op.ne]: true } }, // Check if onApprovedAR is already true ], }, individualHooks: true, From 996a64c8c865f7cc04050f7bdf3fa896dcbd9cab Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Fri, 20 Sep 2024 10:15:43 -0700 Subject: [PATCH 12/22] Update activityReportGoal.test.js --- src/models/hooks/activityReportGoal.test.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/models/hooks/activityReportGoal.test.js b/src/models/hooks/activityReportGoal.test.js index 1d418205b6..f4db5f3778 100644 --- a/src/models/hooks/activityReportGoal.test.js +++ b/src/models/hooks/activityReportGoal.test.js @@ -295,7 +295,15 @@ describe('updateOnARAndOnApprovedARForMergedGoals', () => { expect(sequelize.models.Goal.update).toHaveBeenCalledWith( { onAR: true, onApprovedAR: false }, // onApprovedAR is false since no approved reports { - where: { id: instance.goalId }, + where: { + id: instance.goalId, + [Op.or]: [ + // Ensure onAR condition is in the where clause + { onAR: { [Op.ne]: true } }, + // Ensure onApprovedAR condition is in the where clause + { onApprovedAR: { [Op.ne]: false } }, + ], + }, individualHooks: true, }, ); From b522cdbd6f753ecd1bb61e2258e6ae80cc496fca Mon Sep 17 00:00:00 2001 From: Krys Wisnaskas Date: Fri, 20 Sep 2024 15:37:50 -0400 Subject: [PATCH 13/22] Use xml2js --- package.json | 3 +-- src/lib/updateGrantsRecipients.js | 17 +++++++++++------ yarn.lock | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 21529bb5fe..46684c55b4 100644 --- a/package.json +++ b/package.json @@ -327,7 +327,6 @@ "express-http-context": "^1.2.4", "express-unless": "^1.0.0", "express-winston": "^4.0.5", - "fast-xml-parser": "^4.5.0", "file-type": "^16.5.4", "form-data": "^4.0.0", "he": "^1.2.0", @@ -363,7 +362,7 @@ "wait-for-expect": "^3.0.2", "winston": "^3.3.3", "ws": "^8.17.1", - "xml2json": "^0.12.0", + "xml2js": "^0.6.2", "yargs": "^17.3.1", "yayson": "^2.1.0" } diff --git a/src/lib/updateGrantsRecipients.js b/src/lib/updateGrantsRecipients.js index 5e915f3508..acf1609143 100644 --- a/src/lib/updateGrantsRecipients.js +++ b/src/lib/updateGrantsRecipients.js @@ -1,5 +1,6 @@ import AdmZip from 'adm-zip'; import { XMLParser } from 'fast-xml-parser'; +import xml2js from 'xml2js'; import axios from 'axios'; import { keyBy, mapValues } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; @@ -18,7 +19,11 @@ import { GRANT_PERSONNEL_ROLES } from '../constants'; const fs = require('mz/fs'); -const parser = new XMLParser(); +const parser_xml2js = new xml2js.Parser({ + explicitArray: false, + explicitCharkey: false, + trim: true +}); // TTAHUB-2126 TTAHUB-2334 // Update the specific attribute (e.g., state code) for each grant, @@ -217,7 +222,7 @@ export async function processFiles(hashSumHex) { ); const grantAgencyData = await fs.readFile('./temp/grant_agency.xml'); - const grantAgency = parser.parse(grantAgencyData); + const grantAgency = await parser_xml2js.parseStringPromise(grantAgencyData); // we are only interested in non-delegates const grantRecipients = grantAgency.grant_agencies.grant_agency.filter( (g) => g.grant_agency_number === '0', @@ -225,7 +230,7 @@ export async function processFiles(hashSumHex) { // process recipients aka agencies that are non-delegates const agencyData = await fs.readFile('./temp/agency.xml'); - const agency = parser.parse(agencyData); + const agency = await parser_xml2js.parseStringPromise(agencyData); // filter out delegates by matching to the non-delegates; // filter out recipient 5 (TTAHUB-705) @@ -240,7 +245,7 @@ export async function processFiles(hashSumHex) { // process grants const grantData = await fs.readFile('./temp/grant_award.xml'); - const grant = parser.parse(grantData); + const grant = await parser_xml2js.parseStringPromise(grantData); // temporary workaround for recipient 628 where it's name is coming in as DBA one. // This issue is pending with HSES as of 12/22/22. @@ -268,7 +273,7 @@ export async function processFiles(hashSumHex) { ); const programData = await fs.readFile('./temp/grant_program.xml'); - const programs = parser.parse(programData); + const programs = await parser_xml2js.parseStringPromise(programData); const grantsForDb = grant.grant_awards.grant_award.map((g) => { let { @@ -374,7 +379,7 @@ export async function processFiles(hashSumHex) { // Load and Process grant replacement data. const grantReplacementsData = await fs.readFile('./temp/grant_award_replacement.xml'); - const grantReplacementsJson = parser.parse(grantReplacementsData); + const grantReplacementsJson = await parser_xml2js.parseStringPromise(grantReplacementsData); const grantReplacements = grantReplacementsJson .grant_award_replacements.grant_award_replacement; diff --git a/yarn.lock b/yarn.lock index 150eb97a46..da90f5bb86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11534,6 +11534,11 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +sax@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + sax@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" @@ -13421,6 +13426,13 @@ xdg-basedir@^4.0.0: resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -13439,6 +13451,14 @@ xml2js@0.4.19, xml2js@^0.5.0: sax ">=0.6.0" xmlbuilder "~11.0.0" +xml2js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + xml2json@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/xml2json/-/xml2json-0.12.0.tgz#b2ae450b267033b76d896f86e022fa7bff678572" From 76456ca5146a456677aa610de4d713239f7b1861 Mon Sep 17 00:00:00 2001 From: Krys Wisnaskas Date: Fri, 20 Sep 2024 15:49:22 -0400 Subject: [PATCH 14/22] Fix lint errors --- src/lib/updateGrantsRecipients.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib/updateGrantsRecipients.js b/src/lib/updateGrantsRecipients.js index acf1609143..762bc1b976 100644 --- a/src/lib/updateGrantsRecipients.js +++ b/src/lib/updateGrantsRecipients.js @@ -1,5 +1,4 @@ import AdmZip from 'adm-zip'; -import { XMLParser } from 'fast-xml-parser'; import xml2js from 'xml2js'; import axios from 'axios'; import { keyBy, mapValues } from 'lodash'; @@ -19,10 +18,10 @@ import { GRANT_PERSONNEL_ROLES } from '../constants'; const fs = require('mz/fs'); -const parser_xml2js = new xml2js.Parser({ +const parser = new xml2js.Parser({ explicitArray: false, explicitCharkey: false, - trim: true + trim: true, }); // TTAHUB-2126 TTAHUB-2334 @@ -222,7 +221,7 @@ export async function processFiles(hashSumHex) { ); const grantAgencyData = await fs.readFile('./temp/grant_agency.xml'); - const grantAgency = await parser_xml2js.parseStringPromise(grantAgencyData); + const grantAgency = await parser.parseStringPromise(grantAgencyData); // we are only interested in non-delegates const grantRecipients = grantAgency.grant_agencies.grant_agency.filter( (g) => g.grant_agency_number === '0', @@ -230,7 +229,7 @@ export async function processFiles(hashSumHex) { // process recipients aka agencies that are non-delegates const agencyData = await fs.readFile('./temp/agency.xml'); - const agency = await parser_xml2js.parseStringPromise(agencyData); + const agency = await parser.parseStringPromise(agencyData); // filter out delegates by matching to the non-delegates; // filter out recipient 5 (TTAHUB-705) @@ -245,7 +244,7 @@ export async function processFiles(hashSumHex) { // process grants const grantData = await fs.readFile('./temp/grant_award.xml'); - const grant = await parser_xml2js.parseStringPromise(grantData); + const grant = await parser.parseStringPromise(grantData); // temporary workaround for recipient 628 where it's name is coming in as DBA one. // This issue is pending with HSES as of 12/22/22. @@ -273,7 +272,7 @@ export async function processFiles(hashSumHex) { ); const programData = await fs.readFile('./temp/grant_program.xml'); - const programs = await parser_xml2js.parseStringPromise(programData); + const programs = await parser.parseStringPromise(programData); const grantsForDb = grant.grant_awards.grant_award.map((g) => { let { @@ -379,7 +378,7 @@ export async function processFiles(hashSumHex) { // Load and Process grant replacement data. const grantReplacementsData = await fs.readFile('./temp/grant_award_replacement.xml'); - const grantReplacementsJson = await parser_xml2js.parseStringPromise(grantReplacementsData); + const grantReplacementsJson = await parser.parseStringPromise(grantReplacementsData); const grantReplacements = grantReplacementsJson .grant_award_replacements.grant_award_replacement; From 90a0ff7366cd4ff1541c3bf4522363e0f46d1fcc Mon Sep 17 00:00:00 2001 From: Krys Wisnaskas Date: Fri, 20 Sep 2024 16:42:25 -0400 Subject: [PATCH 15/22] Update known dependencies --- yarn-audit-known-issues | 3 --- 1 file changed, 3 deletions(-) diff --git a/yarn-audit-known-issues b/yarn-audit-known-issues index 752a6baacc..3b73ad385d 100644 --- a/yarn-audit-known-issues +++ b/yarn-audit-known-issues @@ -1,4 +1 @@ {"type":"auditAdvisory","data":{"resolution":{"id":1096366,"path":"email-templates>preview-email>mailparser>nodemailer","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"6.7.3","paths":["email-templates>preview-email>mailparser>nodemailer"]}],"found_by":null,"deleted":null,"references":"- https://github.com/nodemailer/nodemailer/security/advisories/GHSA-9h6g-pr28-7cqp\n- https://gist.github.com/francoatmega/890dd5053375333e40c6fdbcc8c58df6\n- https://gist.github.com/francoatmega/9aab042b0b24968d7b7039818e8b2698\n- https://github.com/nodemailer/nodemailer/commit/dd8f5e8a4ddc99992e31df76bcff9c590035cd4a\n- https://github.com/advisories/GHSA-9h6g-pr28-7cqp","created":"2024-01-31T22:42:54.000Z","id":1096366,"npm_advisory_id":null,"overview":"### Summary\nA ReDoS vulnerability occurs when nodemailer tries to parse img files with the parameter `attachDataUrls` set, causing the stuck of event loop. \nAnother flaw was found when nodemailer tries to parse an attachments with a embedded file, causing the stuck of event loop. \n\n### Details\n\nRegex: /^data:((?:[^;]*;)*(?:[^,]*)),(.*)$/\n\nPath: compile -> getAttachments -> _processDataUrl\n\nRegex: /(]* src\\s*=[\\s\"']*)(data:([^;]+);[^\"'>\\s]+)/\n\nPath: _convertDataImages\n\n### PoC\n\nhttps://gist.github.com/francoatmega/890dd5053375333e40c6fdbcc8c58df6\nhttps://gist.github.com/francoatmega/9aab042b0b24968d7b7039818e8b2698\n\n### Impact\n\nReDoS causes the event loop to stuck a specially crafted evil email can cause this problem.\n","reported_by":null,"title":"nodemailer ReDoS when trying to send a specially crafted email","metadata":null,"cves":[],"access":"public","severity":"moderate","module_name":"nodemailer","vulnerable_versions":"<=6.9.8","github_advisory_id":"GHSA-9h6g-pr28-7cqp","recommendation":"Upgrade to version 6.9.9 or later","patched_versions":">=6.9.9","updated":"2024-02-01T17:58:50.000Z","cvss":{"score":5.3,"vectorString":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L"},"cwe":["CWE-1333"],"url":"https://github.com/advisories/GHSA-9h6g-pr28-7cqp"}}} -{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.3.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","id":1096410,"npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","metadata":null,"cves":["CVE-2020-36604"],"access":"public","severity":"high","module_name":"hoek","vulnerable_versions":"<=6.1.3","github_advisory_id":"GHSA-c429-5p7v-vgjp","recommendation":"None","patched_versions":"<0.0.0","updated":"2024-02-07T18:59:37.000Z","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} -{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>joi>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.3.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","id":1096410,"npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","metadata":null,"cves":["CVE-2020-36604"],"access":"public","severity":"high","module_name":"hoek","vulnerable_versions":"<=6.1.3","github_advisory_id":"GHSA-c429-5p7v-vgjp","recommendation":"None","patched_versions":"<0.0.0","updated":"2024-02-07T18:59:37.000Z","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} -{"type":"auditAdvisory","data":{"resolution":{"id":1096410,"path":"xml2json>joi>topo>hoek","dev":false,"bundled":false,"optional":false},"advisory":{"findings":[{"version":"4.3.1","paths":["xml2json>hoek"]},{"version":"5.0.4","paths":["xml2json>joi>hoek"]},{"version":"6.1.3","paths":["xml2json>joi>topo>hoek"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2020-36604\n- https://github.com/hapijs/hoek/issues/352\n- https://github.com/hapijs/hoek/commit/4d0804bc6135ad72afdc5e1ec002b935b2f5216a\n- https://github.com/hapijs/hoek/commit/948baf98634a5c206875b67d11368f133034fa90\n- https://github.com/advisories/GHSA-c429-5p7v-vgjp","created":"2022-09-25T00:00:27.000Z","id":1096410,"npm_advisory_id":null,"overview":"hoek versions prior to 8.5.1, and 9.x prior to 9.0.3 are vulnerable to prototype pollution in the clone function. If an object with the __proto__ key is passed to clone() the key is converted to a prototype. This issue has been patched in version 9.0.3, and backported to 8.5.1. ","reported_by":null,"title":"hoek subject to prototype pollution via the clone function.","metadata":null,"cves":["CVE-2020-36604"],"access":"public","severity":"high","module_name":"hoek","vulnerable_versions":"<=6.1.3","github_advisory_id":"GHSA-c429-5p7v-vgjp","recommendation":"None","patched_versions":"<0.0.0","updated":"2024-02-07T18:59:37.000Z","cvss":{"score":8.1,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-c429-5p7v-vgjp"}}} From 28576d0a21d91d62dda7a853514e289b6fbe267a Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Mon, 23 Sep 2024 09:59:29 -0400 Subject: [PATCH 16/22] remove comment --- frontend/src/pages/ViewTrainingReport/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/pages/ViewTrainingReport/index.js b/frontend/src/pages/ViewTrainingReport/index.js index 81648272ea..65faa4bacd 100644 --- a/frontend/src/pages/ViewTrainingReport/index.js +++ b/frontend/src/pages/ViewTrainingReport/index.js @@ -218,7 +218,6 @@ export default function ViewTrainingReport({ match }) { }, striped: true, }] : []; - // console.log('sessions: ', event.sessionReports); const generateIstOfficeOrRecipientProperties = (session) => { if (session.data.isIstVisit === 'yes') { From c55adfbea3f8bcdcde575b8306416db9c7d5c81f Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Mon, 23 Sep 2024 11:56:59 -0400 Subject: [PATCH 17/22] move to dev --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 276daf47c9..1dcabf00a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -560,7 +560,7 @@ parameters: type: string dev_git_branch: # change to feature branch to test deployment description: "Name of github branch that will deploy to dev" - default: "al-ttahub-3196-new-tr-views" + default: "al-ttahub-3329-fix-event-view" type: string sandbox_git_branch: # change to feature branch to test deployment default: "kw-fix-duplicate-programs" From 57f4742de722e9e0e54bb37153e5e70b1b370e6e Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 24 Sep 2024 13:14:19 -0400 Subject: [PATCH 18/22] ignore for now --- frontend/yarn-audit-known-issues | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/yarn-audit-known-issues b/frontend/yarn-audit-known-issues index 697c6815af..afca62462a 100644 --- a/frontend/yarn-audit-known-issues +++ b/frontend/yarn-audit-known-issues @@ -3,3 +3,4 @@ {"type":"auditAdvisory","data":{"resolution":{"id":1097682,"path":"react-scripts>jest>jest-cli>@jest/core>jest-config>jest-runner>jest-environment-jsdom>jsdom>tough-cookie","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"4.0.0","paths":["react-scripts>jest>@jest/core>jest-config>jest-environment-jsdom>jsdom>tough-cookie","react-scripts>jest>jest-cli>@jest/core>jest-config>jest-environment-jsdom>jsdom>tough-cookie","react-scripts>jest>jest-cli>@jest/core>jest-config>jest-runner>jest-environment-jsdom>jsdom>tough-cookie"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2023-26136\n- https://github.com/salesforce/tough-cookie/issues/282\n- https://github.com/salesforce/tough-cookie/commit/12d474791bb856004e858fdb1c47b7608d09cf6e\n- https://github.com/salesforce/tough-cookie/releases/tag/v4.1.3\n- https://security.snyk.io/vuln/SNYK-JS-TOUGHCOOKIE-5672873\n- https://lists.debian.org/debian-lts-announce/2023/07/msg00010.html\n- https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3HUE6ZR5SL73KHL7XUPAOEL6SB7HUDT2\n- https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/6PVVPNSAGSDS63HQ74PJ7MZ3MU5IYNVZ\n- https://security.netapp.com/advisory/ntap-20240621-0006\n- https://github.com/advisories/GHSA-72xf-g2v4-qvf3","created":"2023-07-01T06:30:16.000Z","id":1097682,"npm_advisory_id":null,"overview":"Versions of the package tough-cookie before 4.1.3 are vulnerable to Prototype Pollution due to improper handling of Cookies when using CookieJar in `rejectPublicSuffixes=false` mode. This issue arises from the manner in which the objects are initialized.","reported_by":null,"title":"tough-cookie Prototype Pollution vulnerability","metadata":null,"cves":["CVE-2023-26136"],"access":"public","severity":"moderate","module_name":"tough-cookie","vulnerable_versions":"<4.1.3","github_advisory_id":"GHSA-72xf-g2v4-qvf3","recommendation":"Upgrade to version 4.1.3 or later","patched_versions":">=4.1.3","updated":"2024-06-21T21:33:53.000Z","cvss":{"score":6.5,"vectorString":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-72xf-g2v4-qvf3"}}} {"type":"auditAdvisory","data":{"resolution":{"id":1099525,"path":"react-scripts>webpack-dev-server>express>serve-static>send","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"0.18.0","paths":["react-scripts>webpack-dev-server>express>serve-static>send"]}],"found_by":null,"deleted":null,"references":"- https://github.com/pillarjs/send/security/advisories/GHSA-m6fv-jmcg-4jfg\n- https://nvd.nist.gov/vuln/detail/CVE-2024-43799\n- https://github.com/pillarjs/send/commit/ae4f2989491b392ae2ef3b0015a019770ae65d35\n- https://github.com/advisories/GHSA-m6fv-jmcg-4jfg","created":"2024-09-10T19:42:41.000Z","id":1099525,"npm_advisory_id":null,"overview":"### Impact\n\npassing untrusted user input - even after sanitizing it - to `SendStream.redirect()` may execute untrusted code\n\n### Patches\n\nthis issue is patched in send 0.19.0\n\n### Workarounds\n\nusers are encouraged to upgrade to the patched version of express, but otherwise can workaround this issue by making sure any untrusted inputs are safe, ideally by validating them against an explicit allowlist\n\n### Details\n\nsuccessful exploitation of this vector requires the following:\n\n1. The attacker MUST control the input to response.redirect()\n1. express MUST NOT redirect before the template appears\n1. the browser MUST NOT complete redirection before:\n1. the user MUST click on the link in the template\n","reported_by":null,"title":"send vulnerable to template injection that can lead to XSS","metadata":null,"cves":["CVE-2024-43799"],"access":"public","severity":"moderate","module_name":"send","vulnerable_versions":"<0.19.0","github_advisory_id":"GHSA-m6fv-jmcg-4jfg","recommendation":"Upgrade to version 0.19.0 or later","patched_versions":">=0.19.0","updated":"2024-09-10T19:42:42.000Z","cvss":{"score":5,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L"},"cwe":["CWE-79"],"url":"https://github.com/advisories/GHSA-m6fv-jmcg-4jfg"}}} {"type":"auditAdvisory","data":{"resolution":{"id":1099597,"path":"react-admin>ra-ui-materialui>dompurify","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"2.4.4","paths":["react-admin>ra-ui-materialui>dompurify"]}],"found_by":null,"deleted":null,"references":"- https://github.com/cure53/DOMPurify/security/advisories/GHSA-mmhx-hmjr-r674\n- https://github.com/cure53/DOMPurify/commit/1e520262bf4c66b5efda49e2316d6d1246ca7b21\n- https://github.com/cure53/DOMPurify/commit/26e1d69ca7f769f5c558619d644d90dd8bf26ebc\n- https://nvd.nist.gov/vuln/detail/CVE-2024-45801\n- https://github.com/advisories/GHSA-mmhx-hmjr-r674","created":"2024-09-16T20:34:26.000Z","id":1099597,"npm_advisory_id":null,"overview":"It has been discovered that malicious HTML using special nesting techniques can bypass the depth checking added to DOMPurify in recent releases. It was also possible to use Prototype Pollution to weaken the depth check.\n\nThis renders dompurify unable to avoid XSS attack.\n\nFixed by https://github.com/cure53/DOMPurify/commit/1e520262bf4c66b5efda49e2316d6d1246ca7b21 (3.x branch) and https://github.com/cure53/DOMPurify/commit/26e1d69ca7f769f5c558619d644d90dd8bf26ebc (2.x branch).","reported_by":null,"title":"DOMPurify allows tampering by prototype pollution","metadata":null,"cves":["CVE-2024-45801"],"access":"public","severity":"high","module_name":"dompurify","vulnerable_versions":"<2.5.4","github_advisory_id":"GHSA-mmhx-hmjr-r674","recommendation":"Upgrade to version 2.5.4 or later","patched_versions":">=2.5.4","updated":"2024-09-16T22:37:33.000Z","cvss":{"score":7,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L"},"cwe":["CWE-1321","CWE-1333"],"url":"https://github.com/advisories/GHSA-mmhx-hmjr-r674"}}} +{"type":"auditAdvisory","data":{"resolution":{"id":1099718,"path":"react-scripts>workbox-webpack-plugin>workbox-build>rollup","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"2.79.1","paths":["react-scripts>workbox-webpack-plugin>workbox-build>rollup"]}],"found_by":null,"deleted":null,"references":"- https://github.com/rollup/rollup/security/advisories/GHSA-gcx4-mw62-g8wm\n- https://nvd.nist.gov/vuln/detail/CVE-2024-47068\n- https://github.com/rollup/rollup/commit/2ef77c00ec2635d42697cff2c0567ccc8db34fb4\n- https://github.com/rollup/rollup/commit/e2552c9e955e0a61f70f508200ee9f752f85a541\n- https://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L157-L162\n- https://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L180-L185\n- https://github.com/advisories/GHSA-gcx4-mw62-g8wm","created":"2024-09-23T22:11:02.000Z","id":1099718,"npm_advisory_id":null,"overview":"### Summary\n\nA DOM Clobbering vulnerability was discovered in rollup when bundling scripts that use `import.meta.url` or with plugins that emit and reference asset files from code in `cjs`/`umd`/`iife` format. The DOM Clobbering gadget can lead to cross-site scripting (XSS) in web pages where scriptless attacker-controlled HTML elements (e.g., an `img` tag with an unsanitized `name` attribute) are present.\n\nIt's worth noting that similar issues in other popular bundlers like Webpack ([CVE-2024-43788](https://github.com/webpack/webpack/security/advisories/GHSA-4vvj-4cpr-p986)) have been reported, which might serve as a good reference.\n\n### Details\n\n#### Backgrounds\n\nDOM Clobbering is a type of code-reuse attack where the attacker first embeds a piece of non-script, seemingly benign HTML markups in the webpage (e.g. through a post or comment) and leverages the gadgets (pieces of js code) living in the existing javascript code to transform it into executable code. More for information about DOM Clobbering, here are some references:\n\n[1] https://scnps.co/papers/sp23_domclob.pdf\n[2] https://research.securitum.com/xss-in-amp4email-dom-clobbering/\n\n#### Gadget found in `rollup`\n\nA DOM Clobbering vulnerability in `rollup` bundled scripts was identified, particularly when the scripts uses `import.meta` and set output in format of `cjs`/`umd`/`iife`. In such cases, `rollup` replaces meta property with the URL retrieved from `document.currentScript`.\n\nhttps://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L157-L162\n\nhttps://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L180-L185\n\nHowever, this implementation is vulnerable to a DOM Clobbering attack. The `document.currentScript` lookup can be shadowed by an attacker via the browser's named DOM tree element access mechanism. This manipulation allows an attacker to replace the intended script element with a malicious HTML element. When this happens, the `src` attribute of the attacker-controlled element (e.g., an `img` tag ) is used as the URL for importing scripts, potentially leading to the dynamic loading of scripts from an attacker-controlled server.\n\n### PoC\n\nConsidering a website that contains the following `main.js` script, the devloper decides to use the `rollup` to bundle up the program: `rollup main.js --format cjs --file bundle.js`.\n\n```\nvar s = document.createElement('script')\ns.src = import.meta.url + 'extra.js'\ndocument.head.append(s)\n```\n\nThe output `bundle.js` is shown in the following code snippet.\n\n```\n'use strict';\n\nvar _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;\nvar s = document.createElement('script');\ns.src = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && False && _documentCurrentScript.src || new URL('bundle.js', document.baseURI).href)) + 'extra.js';\ndocument.head.append(s);\n```\n\nAdding the `rollup` bundled script, `bundle.js`, as part of the web page source code, the page could load the `extra.js` file from the attacker's domain, `attacker.controlled.server` due to the introduced gadget during bundling. The attacker only needs to insert an `img` tag with the name attribute set to `currentScript`. This can be done through a website's feature that allows users to embed certain script-less HTML (e.g., markdown renderers, web email clients, forums) or via an HTML injection vulnerability in third-party JavaScript loaded on the page.\n\n```\n\n\n\n rollup Example\n \n \n \n\n\n\n\n\n```\n\n### Impact\n\nThis vulnerability can result in cross-site scripting (XSS) attacks on websites that include rollup-bundled files (configured with an output format of `cjs`, `iife`, or `umd` and use `import.meta`) and allow users to inject certain scriptless HTML tags without properly sanitizing the `name` or `id` attributes.\n\n### Patch\n\nPatching the following two functions with type checking would be effective mitigations against DOM Clobbering attack.\n\n```\nconst getRelativeUrlFromDocument = (relativePath: string, umd = false) =>\n\tgetResolveUrl(\n\t\t`'${escapeId(relativePath)}', ${\n\t\t\tumd ? `typeof document === 'undefined' ? location.href : ` : ''\n\t\t}document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || document.baseURI`\n\t);\n```\n\n```\nconst getUrlFromDocument = (chunkId: string, umd = false) =>\n\t`${\n\t\tumd ? `typeof document === 'undefined' ? location.href : ` : ''\n\t}(${DOCUMENT_CURRENT_SCRIPT} && ${DOCUMENT_CURRENT_SCRIPT}.tagName.toUpperCase() === 'SCRIPT' &&${DOCUMENT_CURRENT_SCRIPT}.src || new URL('${escapeId(\n\t\tchunkId\n\t)}', document.baseURI).href)`;\n```\n","reported_by":null,"title":"DOM Clobbering Gadget found in rollup bundled scripts that leads to XSS","metadata":null,"cves":["CVE-2024-47068"],"access":"public","severity":"high","module_name":"rollup","vulnerable_versions":"<3.29.5","github_advisory_id":"GHSA-gcx4-mw62-g8wm","recommendation":"Upgrade to version 3.29.5 or later","patched_versions":">=3.29.5","updated":"2024-09-23T22:11:05.000Z","cvss":{"score":6.4,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:H"},"cwe":["CWE-79"],"url":"https://github.com/advisories/GHSA-gcx4-mw62-g8wm"}}} From 3a3c4c086992eb26e19eb1ba03a7299dd5c07265 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 24 Sep 2024 17:23:07 -0400 Subject: [PATCH 19/22] fix issue where is ist visit is not saved --- .../ViewTrainingReport/__tests__/index.js | 51 +++++++++++++++++++ .../src/pages/ViewTrainingReport/index.js | 11 +++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/ViewTrainingReport/__tests__/index.js b/frontend/src/pages/ViewTrainingReport/__tests__/index.js index ade7976cb0..45ec7a1da9 100644 --- a/frontend/src/pages/ViewTrainingReport/__tests__/index.js +++ b/frontend/src/pages/ViewTrainingReport/__tests__/index.js @@ -666,6 +666,57 @@ describe('ViewTrainingReport', () => { expect(await screen.findByText('12')).toBeInTheDocument(); }); + it('display the correct value for Is IST visit if the value isIstVisit is not set and we have recipients', async () => { + const e = mockEvent(); + e.sessionReports = [{ + ...e.sessionReports[0], + data: { + ...e.sessionReports[0].data, + isIstVisit: null, + }, + }]; + + fetchMock.getOnce('/api/events/id/1?readOnly=true', e); + fetchMock.getOnce('/api/users/names?ids=1', ['USER 1']); + fetchMock.getOnce('/api/users/names?ids=2', ['USER 2']); + + act(() => { + renderTrainingReport(); + }); + + expect(await screen.findByRole('heading', { name: 'Training event report R03-PD-23-1037' })).toBeInTheDocument(); + expect(await screen.findByText('No')).toBeInTheDocument(); + + expect(screen.queryAllByText('IST visit').length).toBe(1); + }); + + it('display the correct value for Is IST visit if the value isIstVisit is not set and we have no recipients', async () => { + const e = mockEvent(); + e.sessionReports = [{ + ...e.sessionReports[0], + data: { + ...e.sessionReports[0].data, + isIstVisit: null, + recipients: [], + regionalOfficeTta: ['office 1', 'office 2'], + }, + }]; + + fetchMock.getOnce('/api/events/id/1?readOnly=true', e); + fetchMock.getOnce('/api/users/names?ids=1', ['USER 1']); + fetchMock.getOnce('/api/users/names?ids=2', ['USER 2']); + + act(() => { + renderTrainingReport(); + }); + + expect(await screen.findByRole('heading', { name: 'Training event report R03-PD-23-1037' })).toBeInTheDocument(); + expect(await screen.findByText('Yes')).toBeInTheDocument(); + + expect(screen.queryAllByText('IST visit').length).toBe(1); + expect(await screen.findByText(/office 1, office 2/i)).toBeInTheDocument(); + }); + describe('formatOwnerName', () => { test('handles an error', () => { const result = formatOwnerName({ eventReportPilotNationalCenterUsers: 123 }); diff --git a/frontend/src/pages/ViewTrainingReport/index.js b/frontend/src/pages/ViewTrainingReport/index.js index 65faa4bacd..0efde1601c 100644 --- a/frontend/src/pages/ViewTrainingReport/index.js +++ b/frontend/src/pages/ViewTrainingReport/index.js @@ -219,8 +219,15 @@ export default function ViewTrainingReport({ match }) { striped: true, }] : []; + const isIstVisit = (session) => { + if (session.data.isIstVisit === 'yes' || (session.data.regionalOfficeTta && session.data.regionalOfficeTta.length > 0)) { + return true; + } + return false; + }; + const generateIstOfficeOrRecipientProperties = (session) => { - if (session.data.isIstVisit === 'yes') { + if (isIstVisit(session)) { return { 'Regional Office/TTA': session.data.regionalOfficeTta.join(', '), }; @@ -279,7 +286,7 @@ export default function ViewTrainingReport({ match }) { heading: 'Participants', striped: true, data: { - 'IST visit': session.data.isIstVisit ? capitalize(session.data.isIstVisit) : '', + 'IST visit': isIstVisit(session) ? 'Yes' : 'No', ...generateIstOfficeOrRecipientProperties(session), 'Delivery method': capitalize(session.data.deliveryMethod || ''), ...generateNumberOfParticipants(session), From 4f5471793259c0d55753af4f4734a6de43daf548 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 24 Sep 2024 18:40:45 -0400 Subject: [PATCH 20/22] fix issues found by Lauren --- .../ViewTrainingReport/__tests__/index.js | 28 +++++++++++++++++-- .../src/pages/ViewTrainingReport/index.js | 6 ++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/ViewTrainingReport/__tests__/index.js b/frontend/src/pages/ViewTrainingReport/__tests__/index.js index 45ec7a1da9..6401b241b4 100644 --- a/frontend/src/pages/ViewTrainingReport/__tests__/index.js +++ b/frontend/src/pages/ViewTrainingReport/__tests__/index.js @@ -73,7 +73,7 @@ const mockEvent = (data = {}) => ({ eventName: 'Health Webinar Series: Oral Health and Dental Care from a Regional and State Perspective', eventOrganizer: 'Regional PD Event (with National Centers)', 'Full Event Title': 'R03 Health Webinar Series: Oral Health and Dental Care from a Regional and State Perspective', - targetPopulations: ['None'], + targetPopulations: ['Tgt Pop 1'], 'Event Duration/# NC Days of Support': 'Series', }, updatedAt: '2023-06-27T13:46:29.884Z', @@ -251,7 +251,7 @@ describe('ViewTrainingReport', () => { expect(screen.getByText('Regional PD Event (with National Centers)')).toBeInTheDocument(); // target populations - expect(screen.getByText('None')).toBeInTheDocument(); + expect(screen.getByText('Tgt Pop 1')).toBeInTheDocument(); // session 1 expect(screen.getByText('Session 1')).toBeInTheDocument(); @@ -717,6 +717,30 @@ describe('ViewTrainingReport', () => { expect(await screen.findByText(/office 1, office 2/i)).toBeInTheDocument(); }); + it('displays none for objectiveResources not set', async () => { + const e = mockEvent(); + e.sessionReports = [{ + ...e.sessionReports[0], + data: { + ...e.sessionReports[0].data, + objectiveResources: [{ value: '' }], + courses: [], + files: [], + }, + }]; + + fetchMock.getOnce('/api/events/id/1?readOnly=true', e); + fetchMock.getOnce('/api/users/names?ids=1', ['USER 1']); + fetchMock.getOnce('/api/users/names?ids=2', ['USER 2']); + + act(() => { + renderTrainingReport(); + }); + + expect(await screen.findByRole('heading', { name: 'Training event report R03-PD-23-1037' })).toBeInTheDocument(); + expect(await screen.queryAllByText('None').length).toBe(3); + }); + describe('formatOwnerName', () => { test('handles an error', () => { const result = formatOwnerName({ eventReportPilotNationalCenterUsers: 123 }); diff --git a/frontend/src/pages/ViewTrainingReport/index.js b/frontend/src/pages/ViewTrainingReport/index.js index 0efde1601c..5e11df5bdb 100644 --- a/frontend/src/pages/ViewTrainingReport/index.js +++ b/frontend/src/pages/ViewTrainingReport/index.js @@ -277,9 +277,9 @@ export default function ViewTrainingReport({ match }) { 'Session objective': session.data.objective, Topics: session.data.objectiveTopics, Trainers: session.data.objectiveTrainers, - 'Resource links': session.data.objectiveResources ? session.data.objectiveResources.map((o) => o.value) : [], - 'iPD Courses': session.data.courses ? session.data.courses.map((o) => o.name) : [], - 'Resource attachments': session.data.files ? session.data.files.map((f) => f.originalFileName) : [], + 'Resource links': session.data.objectiveResources && session.data.objectiveResources.filter((r) => r.value).length ? session.data.objectiveResources.map((o) => o.value) : 'None', + 'iPD Courses': session.data.courses && session.data.courses.length ? session.data.courses.map((o) => o.name) : 'None', + 'Resource attachments': session.data.files && session.data.files.length ? session.data.files.map((f) => f.originalFileName) : 'None', 'Support type': session.data.objectiveSupportType, }, }, { From d44bdf56db11342f547780e8502f1f66f68191f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:07:24 +0000 Subject: [PATCH 21/22] Bump rollup from 2.79.1 to 2.79.2 in /frontend Bumps [rollup](https://github.com/rollup/rollup) from 2.79.1 to 2.79.2. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.79.1...v2.79.2) --- updated-dependencies: - dependency-name: rollup dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d2879d71e9..19f0a2f5b0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -10845,9 +10845,9 @@ rollup-plugin-terser@^7.0.0: terser "^5.0.0" rollup@^2.43.1: - version "2.79.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" - integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== + version "2.79.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.2.tgz#f150e4a5db4b121a21a747d762f701e5e9f49090" + integrity sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ== optionalDependencies: fsevents "~2.3.2" From ff15db131c43c196a4d77058042474cda43ffac3 Mon Sep 17 00:00:00 2001 From: Krys Wisnaskas Date: Fri, 27 Sep 2024 14:02:45 -0400 Subject: [PATCH 22/22] Update known dependencies --- frontend/yarn-audit-known-issues | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/yarn-audit-known-issues b/frontend/yarn-audit-known-issues index afca62462a..697c6815af 100644 --- a/frontend/yarn-audit-known-issues +++ b/frontend/yarn-audit-known-issues @@ -3,4 +3,3 @@ {"type":"auditAdvisory","data":{"resolution":{"id":1097682,"path":"react-scripts>jest>jest-cli>@jest/core>jest-config>jest-runner>jest-environment-jsdom>jsdom>tough-cookie","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"4.0.0","paths":["react-scripts>jest>@jest/core>jest-config>jest-environment-jsdom>jsdom>tough-cookie","react-scripts>jest>jest-cli>@jest/core>jest-config>jest-environment-jsdom>jsdom>tough-cookie","react-scripts>jest>jest-cli>@jest/core>jest-config>jest-runner>jest-environment-jsdom>jsdom>tough-cookie"]}],"found_by":null,"deleted":null,"references":"- https://nvd.nist.gov/vuln/detail/CVE-2023-26136\n- https://github.com/salesforce/tough-cookie/issues/282\n- https://github.com/salesforce/tough-cookie/commit/12d474791bb856004e858fdb1c47b7608d09cf6e\n- https://github.com/salesforce/tough-cookie/releases/tag/v4.1.3\n- https://security.snyk.io/vuln/SNYK-JS-TOUGHCOOKIE-5672873\n- https://lists.debian.org/debian-lts-announce/2023/07/msg00010.html\n- https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3HUE6ZR5SL73KHL7XUPAOEL6SB7HUDT2\n- https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/6PVVPNSAGSDS63HQ74PJ7MZ3MU5IYNVZ\n- https://security.netapp.com/advisory/ntap-20240621-0006\n- https://github.com/advisories/GHSA-72xf-g2v4-qvf3","created":"2023-07-01T06:30:16.000Z","id":1097682,"npm_advisory_id":null,"overview":"Versions of the package tough-cookie before 4.1.3 are vulnerable to Prototype Pollution due to improper handling of Cookies when using CookieJar in `rejectPublicSuffixes=false` mode. This issue arises from the manner in which the objects are initialized.","reported_by":null,"title":"tough-cookie Prototype Pollution vulnerability","metadata":null,"cves":["CVE-2023-26136"],"access":"public","severity":"moderate","module_name":"tough-cookie","vulnerable_versions":"<4.1.3","github_advisory_id":"GHSA-72xf-g2v4-qvf3","recommendation":"Upgrade to version 4.1.3 or later","patched_versions":">=4.1.3","updated":"2024-06-21T21:33:53.000Z","cvss":{"score":6.5,"vectorString":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N"},"cwe":["CWE-1321"],"url":"https://github.com/advisories/GHSA-72xf-g2v4-qvf3"}}} {"type":"auditAdvisory","data":{"resolution":{"id":1099525,"path":"react-scripts>webpack-dev-server>express>serve-static>send","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"0.18.0","paths":["react-scripts>webpack-dev-server>express>serve-static>send"]}],"found_by":null,"deleted":null,"references":"- https://github.com/pillarjs/send/security/advisories/GHSA-m6fv-jmcg-4jfg\n- https://nvd.nist.gov/vuln/detail/CVE-2024-43799\n- https://github.com/pillarjs/send/commit/ae4f2989491b392ae2ef3b0015a019770ae65d35\n- https://github.com/advisories/GHSA-m6fv-jmcg-4jfg","created":"2024-09-10T19:42:41.000Z","id":1099525,"npm_advisory_id":null,"overview":"### Impact\n\npassing untrusted user input - even after sanitizing it - to `SendStream.redirect()` may execute untrusted code\n\n### Patches\n\nthis issue is patched in send 0.19.0\n\n### Workarounds\n\nusers are encouraged to upgrade to the patched version of express, but otherwise can workaround this issue by making sure any untrusted inputs are safe, ideally by validating them against an explicit allowlist\n\n### Details\n\nsuccessful exploitation of this vector requires the following:\n\n1. The attacker MUST control the input to response.redirect()\n1. express MUST NOT redirect before the template appears\n1. the browser MUST NOT complete redirection before:\n1. the user MUST click on the link in the template\n","reported_by":null,"title":"send vulnerable to template injection that can lead to XSS","metadata":null,"cves":["CVE-2024-43799"],"access":"public","severity":"moderate","module_name":"send","vulnerable_versions":"<0.19.0","github_advisory_id":"GHSA-m6fv-jmcg-4jfg","recommendation":"Upgrade to version 0.19.0 or later","patched_versions":">=0.19.0","updated":"2024-09-10T19:42:42.000Z","cvss":{"score":5,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L"},"cwe":["CWE-79"],"url":"https://github.com/advisories/GHSA-m6fv-jmcg-4jfg"}}} {"type":"auditAdvisory","data":{"resolution":{"id":1099597,"path":"react-admin>ra-ui-materialui>dompurify","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"2.4.4","paths":["react-admin>ra-ui-materialui>dompurify"]}],"found_by":null,"deleted":null,"references":"- https://github.com/cure53/DOMPurify/security/advisories/GHSA-mmhx-hmjr-r674\n- https://github.com/cure53/DOMPurify/commit/1e520262bf4c66b5efda49e2316d6d1246ca7b21\n- https://github.com/cure53/DOMPurify/commit/26e1d69ca7f769f5c558619d644d90dd8bf26ebc\n- https://nvd.nist.gov/vuln/detail/CVE-2024-45801\n- https://github.com/advisories/GHSA-mmhx-hmjr-r674","created":"2024-09-16T20:34:26.000Z","id":1099597,"npm_advisory_id":null,"overview":"It has been discovered that malicious HTML using special nesting techniques can bypass the depth checking added to DOMPurify in recent releases. It was also possible to use Prototype Pollution to weaken the depth check.\n\nThis renders dompurify unable to avoid XSS attack.\n\nFixed by https://github.com/cure53/DOMPurify/commit/1e520262bf4c66b5efda49e2316d6d1246ca7b21 (3.x branch) and https://github.com/cure53/DOMPurify/commit/26e1d69ca7f769f5c558619d644d90dd8bf26ebc (2.x branch).","reported_by":null,"title":"DOMPurify allows tampering by prototype pollution","metadata":null,"cves":["CVE-2024-45801"],"access":"public","severity":"high","module_name":"dompurify","vulnerable_versions":"<2.5.4","github_advisory_id":"GHSA-mmhx-hmjr-r674","recommendation":"Upgrade to version 2.5.4 or later","patched_versions":">=2.5.4","updated":"2024-09-16T22:37:33.000Z","cvss":{"score":7,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L"},"cwe":["CWE-1321","CWE-1333"],"url":"https://github.com/advisories/GHSA-mmhx-hmjr-r674"}}} -{"type":"auditAdvisory","data":{"resolution":{"id":1099718,"path":"react-scripts>workbox-webpack-plugin>workbox-build>rollup","dev":false,"optional":false,"bundled":false},"advisory":{"findings":[{"version":"2.79.1","paths":["react-scripts>workbox-webpack-plugin>workbox-build>rollup"]}],"found_by":null,"deleted":null,"references":"- https://github.com/rollup/rollup/security/advisories/GHSA-gcx4-mw62-g8wm\n- https://nvd.nist.gov/vuln/detail/CVE-2024-47068\n- https://github.com/rollup/rollup/commit/2ef77c00ec2635d42697cff2c0567ccc8db34fb4\n- https://github.com/rollup/rollup/commit/e2552c9e955e0a61f70f508200ee9f752f85a541\n- https://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L157-L162\n- https://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L180-L185\n- https://github.com/advisories/GHSA-gcx4-mw62-g8wm","created":"2024-09-23T22:11:02.000Z","id":1099718,"npm_advisory_id":null,"overview":"### Summary\n\nA DOM Clobbering vulnerability was discovered in rollup when bundling scripts that use `import.meta.url` or with plugins that emit and reference asset files from code in `cjs`/`umd`/`iife` format. The DOM Clobbering gadget can lead to cross-site scripting (XSS) in web pages where scriptless attacker-controlled HTML elements (e.g., an `img` tag with an unsanitized `name` attribute) are present.\n\nIt's worth noting that similar issues in other popular bundlers like Webpack ([CVE-2024-43788](https://github.com/webpack/webpack/security/advisories/GHSA-4vvj-4cpr-p986)) have been reported, which might serve as a good reference.\n\n### Details\n\n#### Backgrounds\n\nDOM Clobbering is a type of code-reuse attack where the attacker first embeds a piece of non-script, seemingly benign HTML markups in the webpage (e.g. through a post or comment) and leverages the gadgets (pieces of js code) living in the existing javascript code to transform it into executable code. More for information about DOM Clobbering, here are some references:\n\n[1] https://scnps.co/papers/sp23_domclob.pdf\n[2] https://research.securitum.com/xss-in-amp4email-dom-clobbering/\n\n#### Gadget found in `rollup`\n\nA DOM Clobbering vulnerability in `rollup` bundled scripts was identified, particularly when the scripts uses `import.meta` and set output in format of `cjs`/`umd`/`iife`. In such cases, `rollup` replaces meta property with the URL retrieved from `document.currentScript`.\n\nhttps://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L157-L162\n\nhttps://github.com/rollup/rollup/blob/b86ffd776cfa906573d36c3f019316d02445d9ef/src/ast/nodes/MetaProperty.ts#L180-L185\n\nHowever, this implementation is vulnerable to a DOM Clobbering attack. The `document.currentScript` lookup can be shadowed by an attacker via the browser's named DOM tree element access mechanism. This manipulation allows an attacker to replace the intended script element with a malicious HTML element. When this happens, the `src` attribute of the attacker-controlled element (e.g., an `img` tag ) is used as the URL for importing scripts, potentially leading to the dynamic loading of scripts from an attacker-controlled server.\n\n### PoC\n\nConsidering a website that contains the following `main.js` script, the devloper decides to use the `rollup` to bundle up the program: `rollup main.js --format cjs --file bundle.js`.\n\n```\nvar s = document.createElement('script')\ns.src = import.meta.url + 'extra.js'\ndocument.head.append(s)\n```\n\nThe output `bundle.js` is shown in the following code snippet.\n\n```\n'use strict';\n\nvar _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;\nvar s = document.createElement('script');\ns.src = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && False && _documentCurrentScript.src || new URL('bundle.js', document.baseURI).href)) + 'extra.js';\ndocument.head.append(s);\n```\n\nAdding the `rollup` bundled script, `bundle.js`, as part of the web page source code, the page could load the `extra.js` file from the attacker's domain, `attacker.controlled.server` due to the introduced gadget during bundling. The attacker only needs to insert an `img` tag with the name attribute set to `currentScript`. This can be done through a website's feature that allows users to embed certain script-less HTML (e.g., markdown renderers, web email clients, forums) or via an HTML injection vulnerability in third-party JavaScript loaded on the page.\n\n```\n\n\n\n rollup Example\n \n \n \n\n\n\n\n\n```\n\n### Impact\n\nThis vulnerability can result in cross-site scripting (XSS) attacks on websites that include rollup-bundled files (configured with an output format of `cjs`, `iife`, or `umd` and use `import.meta`) and allow users to inject certain scriptless HTML tags without properly sanitizing the `name` or `id` attributes.\n\n### Patch\n\nPatching the following two functions with type checking would be effective mitigations against DOM Clobbering attack.\n\n```\nconst getRelativeUrlFromDocument = (relativePath: string, umd = false) =>\n\tgetResolveUrl(\n\t\t`'${escapeId(relativePath)}', ${\n\t\t\tumd ? `typeof document === 'undefined' ? location.href : ` : ''\n\t\t}document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || document.baseURI`\n\t);\n```\n\n```\nconst getUrlFromDocument = (chunkId: string, umd = false) =>\n\t`${\n\t\tumd ? `typeof document === 'undefined' ? location.href : ` : ''\n\t}(${DOCUMENT_CURRENT_SCRIPT} && ${DOCUMENT_CURRENT_SCRIPT}.tagName.toUpperCase() === 'SCRIPT' &&${DOCUMENT_CURRENT_SCRIPT}.src || new URL('${escapeId(\n\t\tchunkId\n\t)}', document.baseURI).href)`;\n```\n","reported_by":null,"title":"DOM Clobbering Gadget found in rollup bundled scripts that leads to XSS","metadata":null,"cves":["CVE-2024-47068"],"access":"public","severity":"high","module_name":"rollup","vulnerable_versions":"<3.29.5","github_advisory_id":"GHSA-gcx4-mw62-g8wm","recommendation":"Upgrade to version 3.29.5 or later","patched_versions":">=3.29.5","updated":"2024-09-23T22:11:05.000Z","cvss":{"score":6.4,"vectorString":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:H"},"cwe":["CWE-79"],"url":"https://github.com/advisories/GHSA-gcx4-mw62-g8wm"}}}