@trackunit/iris-app-playwright 0.2.115 → 0.2.116-alpha-abe4978141a.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/migrations/entry.js.map +1 -0
- package/package.json +1 -1
- package/src/credentials/guards.js.map +1 -0
- package/src/credentials/keys.js.map +1 -0
- package/src/credentials/resolveE2EPassword.js.map +1 -0
- package/src/fixtures/acceptTerms.d.ts +74 -0
- package/src/fixtures/acceptTerms.js +209 -0
- package/src/fixtures/acceptTerms.js.map +1 -0
- package/src/fixtures/baseTest.js.map +1 -0
- package/src/fixtures/describe.js.map +1 -0
- package/src/fixtures/featureFlags.fixture.js.map +1 -0
- package/src/fixtures/harRecording.fixture.js.map +1 -0
- package/src/fixtures/hostReadiness.js.map +1 -0
- package/src/fixtures/hydrateStorageState.js.map +1 -0
- package/src/fixtures/irisApp.fixture.js.map +1 -0
- package/src/fixtures/localDevMode.fixture.js.map +1 -0
- package/src/fixtures/login.fixture.d.ts +68 -7
- package/src/fixtures/login.fixture.js +263 -42
- package/src/fixtures/login.fixture.js.map +1 -0
- package/src/fixtures/loginCacheKey.js.map +1 -0
- package/src/fixtures/resolveCredentials.js.map +1 -0
- package/src/fixtures/withTimeout.js.map +1 -0
- package/src/generators/playwright-configuration/generator.js.map +1 -0
- package/src/index.js.map +1 -0
- package/src/plugins/createLogFile.js.map +1 -0
- package/src/plugins/defaultPlaywrightConfig.js.map +1 -0
- package/src/plugins/harRecordingPath.js.map +1 -0
- package/src/plugins/logsReporter.js.map +1 -0
- package/src/plugins/nxPreset.js.map +1 -0
- package/src/plugins/redactHar.js.map +1 -0
- package/src/plugins/redactPatterns.js.map +1 -0
- package/src/plugins/redactSensitive.js.map +1 -0
- package/src/plugins/redactTrace.js.map +1 -0
- package/src/plugins/scanArtifactsForSecrets.js.map +1 -0
- package/src/plugins/setupPlugins.js.map +1 -0
- package/src/plugins/writeFileWithPrettier.js.map +1 -0
- package/src/support.js.map +1 -0
- package/src/utils/Codeowner.js.map +1 -0
- package/src/utils/fileNameBuilder.js.map +1 -0
- package/src/utils/fileUpdater.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logsReporter.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/logsReporter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FullConfig, FullResult, Reporter, Suite, TestCase, TestResult } from \"@playwright/test/reporter\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"fs\";\nimport * as path from \"path\";\nimport { fileNameBuilder } from \"../utils/fileNameBuilder\";\nimport { createLogFile } from \"./createLogFile\";\nimport { harRecordingPath } from \"./harRecordingPath\";\nimport { redactHarFile } from \"./redactHar\";\nimport { redactSensitive } from \"./redactSensitive\";\nimport { redactTraceFile } from \"./redactTrace\";\nimport type { Formatter } from \"./writeFileWithPrettier\";\n\nexport interface LogsReporterOptions {\n /** Directory where log files are written on test failure */\n logsFolder: string;\n /** Directory where HAR files are moved on test failure (temp HAR deleted on pass) */\n harsFolder?: string;\n /** Workspace root used by createLogFile to locate prettier.config.mjs */\n nxRoot: string;\n /** Prettier formatter instance */\n formatter: Formatter;\n}\n\n/**\n * Custom Playwright reporter that writes redacted log files on test failure\n * and manages HAR file cleanup (retain on failure, delete on pass).\n */\nexport class LogsReporter implements Reporter {\n private readonly logsFolder: string;\n private readonly harsFolder: string | undefined;\n private readonly nxRoot: string;\n private readonly formatter: Formatter;\n /**\n * Promises of in-flight log file writes from `onTestEnd`. `createLogFile`\n * formats with Prettier (async) and writes via `writeFileSync`; Playwright\n * may finish the run before those formats settle, so we track them here and\n * await them in `onEnd` to surface formatter errors and ensure files exist\n * by the time the process exits.\n */\n private readonly pendingWrites: Array<Promise<void>> = [];\n\n /** @param options - Reporter configuration options */\n public constructor(options: LogsReporterOptions) {\n this.logsFolder = options.logsFolder;\n this.harsFolder = options.harsFolder;\n this.nxRoot = options.nxRoot;\n this.formatter = options.formatter;\n }\n\n /** Creates the logs folder before the run begins. */\n public onBegin(_config: FullConfig, _suite: Suite): void {\n if (!existsSync(this.logsFolder)) {\n mkdirSync(this.logsFolder, { recursive: true });\n }\n }\n\n /** Writes a redacted log file on test failure and manages HAR file retention. */\n public onTestEnd(test: TestCase, result: TestResult): void {\n const titlePath = test.titlePath().filter(t => t.length > 0);\n const describeTitle = titlePath.slice(0, -1).join(\" > \");\n const testTitle = test.title;\n const fullTitle = describeTitle ? `${describeTitle} - ${testTitle}` : testTitle;\n\n const isFailure = result.status === \"failed\" || result.status === \"timedOut\";\n\n if (isFailure) {\n const fileName = fileNameBuilder(fullTitle, \"failed\", result.retry);\n const logs = result.stdout.concat(result.stderr).map(entry => {\n const message = typeof entry === \"string\" ? entry : entry.toString();\n return {\n type: \"stdout\",\n message: redactSensitive(message),\n severity: result.status === \"failed\" ? \"error\" : \"warning\",\n };\n });\n\n this.pendingWrites.push(createLogFile(this.nxRoot, this.logsFolder, fileName, logs, this.formatter));\n\n // Defense in depth for trace artifacts: `login()`'s slow path does\n // `page.goto(\"/auth/manager-classic#session_token=...\")`, and Playwright\n // records the full `goto` URL as the action argument. With\n // `trace: \"retain-on-failure\"` from `defaultPlaywrightConfig`, a live\n // Okta session token would otherwise persist in the retained trace zip\n // alongside the test artifacts. The fragment is client-side only — it\n // never reaches the HTTP server and HAR/log redaction already covers\n // everything that does — so this pass scrubs the remaining surface.\n const tracePath = this.getResultTracePath(result);\n if (tracePath !== undefined && existsSync(tracePath)) {\n this.pendingWrites.push(redactTraceFile(tracePath));\n }\n\n this.redactErrorContext(result);\n }\n\n this.persistHar(fullTitle, test, result, isFailure);\n }\n\n /**\n * Awaits in-flight log writes started in `onTestEnd` before the reporter\n * signals completion. Without this, Playwright may exit before Prettier\n * settles, leaving partial or missing log files and swallowing formatter\n * errors.\n */\n public async onEnd(_result: FullResult): Promise<void> {\n await Promise.all(this.pendingWrites);\n }\n\n /** Returns false so Playwright adds its own terminal reporter alongside this one. */\n public printsToStdio(): boolean {\n return false;\n }\n\n /**\n * Scrubs Playwright's `error-context.md` failure attachment in place.\n *\n * On failure Playwright writes an `error-context.md` under `test-results/`\n * (uploaded as a CI artifact) that embeds the failing assertion's *received*\n * value and a page/aria snapshot. The `login()` slow path navigates to\n * `/auth/manager-classic#session_token=...`, so this file carries a live Okta\n * session token — and would carry the password if it ever surfaced in an\n * assertion's received value. It is neither a trace, HAR, nor a stdout log, so\n * the other redaction passes miss it entirely; scrub it here. Best-effort:\n * redaction never fails the reporter.\n */\n private redactErrorContext(result: TestResult): void {\n for (const attachment of result.attachments) {\n const attachmentPath = attachment.path;\n if (attachmentPath === undefined || !attachmentPath.endsWith(\"error-context.md\") || !existsSync(attachmentPath)) {\n continue;\n }\n try {\n writeFileSync(attachmentPath, redactSensitive(readFileSync(attachmentPath, \"utf-8\")));\n } catch {\n /* Redaction is best-effort; never fail the reporter over it. */\n }\n }\n }\n\n private getResultTracePath(result: TestResult): string | undefined {\n const attachment = result.attachments.find(a => a.name === \"trace\" && a.path !== undefined);\n return attachment?.path;\n }\n\n /**\n * Persists (on failure) or cleans up (always) the per-test HAR recorded by the\n * `harRecording` fixture.\n *\n * The raw HAR is recorded to a deterministic temp path outside `outputDir`\n * (see `harRecordingPath`). `recordHar` only flushes it when the browser\n * context closes — and on a hard timeout that close completes during\n * Playwright's *native* context teardown, after the fixture's own teardown has\n * bounded out. Because `onTestEnd` runs after all teardown, the file is\n * reliably present here regardless of how late the close flushed: this is what\n * makes a hard-timeout HAR survive (the earlier `testInfo.attach` seam ran too\n * early on that path and lost it). On failure we redact (the HAR carries\n * Authorization/Cookie and request/response bodies) and move it into the shared\n * `hars/` artifacts folder; in every case we delete the raw temp source via\n * `finally` so an unredacted HAR never lingers on disk, even if redaction throws.\n */\n private persistHar(fullTitle: string, test: TestCase, result: TestResult, isFailure: boolean): void {\n const harPath = harRecordingPath({ testId: test.id, retry: result.retry });\n if (!existsSync(harPath)) {\n return;\n }\n try {\n if (isFailure && this.harsFolder !== undefined) {\n if (!existsSync(this.harsFolder)) {\n mkdirSync(this.harsFolder, { recursive: true });\n }\n const harFileName = `${fileNameBuilder(fullTitle, \"failed\", result.retry)}.har`;\n redactHarFile(harPath, path.join(this.harsFolder, harFileName));\n }\n } finally {\n rmSync(harPath, { force: true });\n }\n }\n}\n\nexport default LogsReporter;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nxPreset.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/nxPreset.ts"],"names":[],"mappings":";;AASA,wCAIC;AAbD,kDAAoD;AAEpD;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,QAAgB;IAC7C,OAAO,IAAA,oBAAW,EAAC,QAAQ,EAAE;QAC3B,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { nxE2EPreset } from \"@nx/playwright/preset\";\n\n/**\n * Creates the standard NX E2E preset configuration for Playwright.\n * Wraps the `@nx/playwright` preset with Trackunit defaults.\n *\n * @param filename - Pass `__filename` from the calling `playwright.config.ts`\n * @returns {ReturnType<typeof nxE2EPreset>} NX Playwright preset configuration\n */\nexport function createNxPreset(filename: string): ReturnType<typeof nxE2EPreset> {\n return nxE2EPreset(filename, {\n testDir: \"./src\",\n });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactHar.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/redactHar.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, writeFileSync } from \"fs\";\nimport { redactSensitive, SENSITIVE_KEYS } from \"./redactSensitive\";\n\nconst SENSITIVE_HEADER_NAMES = new Set(\n [...SENSITIVE_KEYS, \"authorization\", \"cookie\", \"set-cookie\", \"x-api-key\", \"x-auth-token\", \"proxy-authorization\"].map(\n n => n.toLowerCase()\n )\n);\n\ninterface HarHeader {\n name?: unknown;\n value?: unknown;\n}\n\ninterface HarCookie {\n name?: unknown;\n value?: unknown;\n}\n\ninterface HarPostData {\n text?: unknown;\n mimeType?: unknown;\n params?: unknown;\n}\n\ninterface HarRequest {\n url?: unknown;\n headers?: unknown;\n cookies?: unknown;\n queryString?: unknown;\n postData?: unknown;\n}\n\ninterface HarResponse {\n url?: unknown;\n headers?: unknown;\n cookies?: unknown;\n content?: unknown;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst redactHeaderArray = (headers: unknown): unknown => {\n if (!Array.isArray(headers)) return headers;\n return headers.map((h: HarHeader) => {\n if (!isRecord(h)) return h;\n const name = typeof h.name === \"string\" ? h.name : \"\";\n if (SENSITIVE_HEADER_NAMES.has(name.toLowerCase())) {\n return { ...h, value: \"***\" };\n }\n return h;\n });\n};\n\nconst redactCookieArray = (cookies: unknown): unknown => {\n if (!Array.isArray(cookies)) return cookies;\n return cookies.map((c: HarCookie) => (isRecord(c) ? { ...c, value: \"***\" } : c));\n};\n\nconst redactQueryString = (qs: unknown): unknown => {\n if (!Array.isArray(qs)) return qs;\n return qs.map(entry => {\n if (!isRecord(entry)) return entry;\n const name = typeof entry.name === \"string\" ? entry.name : \"\";\n if (SENSITIVE_HEADER_NAMES.has(name.toLowerCase())) {\n return { ...entry, value: \"***\" };\n }\n return entry;\n });\n};\n\nconst redactStringField = (value: unknown): unknown => (typeof value === \"string\" ? redactSensitive(value) : value);\n\nconst redactPostData = (postData: unknown): unknown => {\n if (!isRecord(postData)) return postData;\n const next: HarPostData & Record<string, unknown> = { ...postData };\n if (typeof postData.text === \"string\") {\n next.text = redactSensitive(postData.text);\n }\n if (Array.isArray(postData.params)) {\n next.params = postData.params.map(p => {\n if (!isRecord(p)) return p;\n const name = typeof p.name === \"string\" ? p.name : \"\";\n if (SENSITIVE_HEADER_NAMES.has(name.toLowerCase())) {\n return { ...p, value: \"***\" };\n }\n return p;\n });\n }\n return next;\n};\n\nconst redactContent = (content: unknown): unknown => {\n if (!isRecord(content)) return content;\n if (typeof content.text === \"string\") {\n return { ...content, text: redactSensitive(content.text) };\n }\n return content;\n};\n\nconst redactRequest = (request: unknown): unknown => {\n if (!isRecord(request)) return request;\n const next: HarRequest & Record<string, unknown> = { ...request };\n next.url = redactStringField(request.url);\n next.headers = redactHeaderArray(request.headers);\n next.cookies = redactCookieArray(request.cookies);\n next.queryString = redactQueryString(request.queryString);\n next.postData = redactPostData(request.postData);\n return next;\n};\n\nconst redactResponse = (response: unknown): unknown => {\n if (!isRecord(response)) return response;\n const next: HarResponse & Record<string, unknown> = { ...response };\n next.url = redactStringField(response.url);\n next.headers = redactHeaderArray(response.headers);\n next.cookies = redactCookieArray(response.cookies);\n next.content = redactContent(response.content);\n return next;\n};\n\n/**\n * Strips sensitive headers, cookies, query parameters, and bodies from a HAR document.\n * Returns a new object — the input is not mutated.\n *\n * Redacts:\n * - Headers matching SENSITIVE_HEADER_NAMES (Authorization, Cookie, Set-Cookie, etc.)\n * - All cookie values (request & response)\n * - Query-string parameters with sensitive names\n * - Form params with sensitive names\n * - JSON-like text in request/response bodies via redactSensitive\n */\nexport const redactHar = (har: unknown): unknown => {\n if (!isRecord(har)) return har;\n const log = har.log;\n if (!isRecord(log)) return har;\n const entries = log.entries;\n if (!Array.isArray(entries)) return har;\n\n const redactedEntries = entries.map(entry => {\n if (!isRecord(entry)) return entry;\n return {\n ...entry,\n request: redactRequest(entry.request),\n response: redactResponse(entry.response),\n };\n });\n\n return {\n ...har,\n log: {\n ...log,\n entries: redactedEntries,\n },\n };\n};\n\n/**\n * Reads a HAR file, redacts sensitive content, and writes it to a new path.\n * The source file is unchanged. Returns the bytes written.\n */\nexport const redactHarFile = (sourcePath: string, destPath: string): void => {\n const raw = readFileSync(sourcePath, \"utf-8\");\n const parsed: unknown = JSON.parse(raw);\n const redacted = redactHar(parsed);\n writeFileSync(destPath, JSON.stringify(redacted));\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactPatterns.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/redactPatterns.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH;;;;;GAKG;AACU,QAAA,GAAG,GAAG,WAAW,CAAC;AAC/B,+CAA+C;AAClC,QAAA,KAAK,GAAG,GAAG,WAAG,GAAG,CAAC;AAC/B,iDAAiD;AACpC,QAAA,QAAQ,GAAG,KAAK,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,GAAW,EAAE,CAAC,aAAa,CAAC;AAEtD,MAAM,iBAAiB,GAAG,IAAI,MAAM,CAAC,GAAG,aAAK,QAAQ,aAAK,YAAY,aAAK,EAAE,EAAE,GAAG,CAAC,CAAC;AACpF,MAAM,mBAAmB,GAAG,IAAI,MAAM,CAAC,GAAG,aAAK,MAAM,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,aAAK,OAAO,EAAE,GAAG,CAAC,CAAC;AAEpG,uFAAuF;AACvF,MAAM,kBAAkB,GAAG,GAAW,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,aAAK,QAAQ,aAAK,YAAY,aAAK,UAAU,aAAK,GAAG,EAAE,IAAI,CAAC,CAAC;AAErH;;;;GAIG;AACH,MAAM,cAAc,GAAG,CAAC,UAAkB,EAAW,EAAE,CACrD,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAE7E,0EAA0E;AACnE,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAU,EAAE,CACzD,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,UAAU,CAAC,EAAE,CAC7C,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,KAAK,gBAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CACtG,CAAC;AAHS,QAAA,kBAAkB,sBAG3B;AAEJ;;;;;;;GAOG;AACI,MAAM,YAAY,GAAG,CAAC,IAAY,EAAiB,EAAE;IAC1D,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,iBAAiB,EAAE,CAAC,EAAE,CAAC;QAC7D,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;YAChC,SAAS;QACX,CAAC;QACD,KAAK,MAAM,UAAU,IAAI,UAAU,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,EAAE,CAAC;YACnE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAZW,QAAA,YAAY,gBAYvB","sourcesContent":["/**\n * Redaction/detection primitives shared by the redactor (`redactSensitive`) and\n * the structural scanner (`scanArtifactsForUnredactedSecrets`) so the two\n * recognise the same shapes and cannot drift. In particular, browser-cookie\n * detection is centralised here: it must be property-order independent (a\n * value-last cookie is still a cookie) and must not misfire on ordinary\n * `{ name, value, path }` structures.\n */\n\n/**\n * Matches any run of backslashes preceding a quote. A JSON string nested inside\n * another escapes its quotes once per level of nesting (`\"` → `\\\"` → `\\\\\\\"` →\n * …), so the escape prefix grows without bound; matching an arbitrary run makes\n * the patterns escape-depth agnostic.\n */\nexport const ESC = \"(?:\\\\\\\\)*\";\n/** A JSON double-quote at any escape depth. */\nexport const QUOTE = `${ESC}\"`;\n/** The marker a redacted value is reduced to. */\nexport const REDACTED = \"***\";\n\n/**\n * Strong, cookie-specific marker keys. A Playwright `storageState` cookie always\n * carries these; generic keys like `path`/`expires` are deliberately excluded so\n * an ordinary `{ name, value, path }` object is not mistaken for a cookie\n * (which would redact useful debugging data and produce false positives).\n */\nconst COOKIE_MARKERS = [\"domain\", \"httpOnly\", \"sameSite\", \"secure\"];\n\n/**\n * Matches a flat JSON object literal (no nested braces) — one `addCookies`\n * entry. JSON only escapes `\"` and `\\`, never braces, so this still isolates a\n * cookie object at any escape depth.\n */\nconst flatObjectPattern = (): RegExp => /\\{[^{}]*\\}/g;\n\nconst valueFieldPattern = new RegExp(`${QUOTE}value${QUOTE}\\\\s*:\\\\s*${QUOTE}`, \"i\");\nconst cookieMarkerPattern = new RegExp(`${QUOTE}(?:${COOKIE_MARKERS.join(\"|\")})${QUOTE}\\\\s*:`, \"i\");\n\n/** Captures the `value` string of an object (group 2), for redaction or inspection. */\nconst cookieValuePattern = (): RegExp => new RegExp(`(${QUOTE}value${QUOTE}\\\\s*:\\\\s*${QUOTE})(.*?)(${QUOTE})`, \"gi\");\n\n/**\n * A flat JSON object is treated as a browser cookie when it carries a `value`\n * field together with a strong cookie marker — evaluated over the whole object,\n * so property order does not matter and a value-last cookie is still detected.\n */\nconst isCookieObject = (flatObject: string): boolean =>\n valueFieldPattern.test(flatObject) && cookieMarkerPattern.test(flatObject);\n\n/** Redacts the `value` of every browser-cookie object found in `text`. */\nexport const redactCookieValues = (text: string): string =>\n text.replace(flatObjectPattern(), flatObject =>\n isCookieObject(flatObject) ? flatObject.replace(cookieValuePattern(), `$1${REDACTED}$3`) : flatObject\n );\n\n/**\n * The `value` of every browser-cookie object in `text` (after any redaction has\n * run). The structural scanner treats a cookie whose value is not `REDACTED` as\n * a leak.\n *\n * @param text - Text to inspect (a trace stream, resource body, or plain file).\n * @returns {Array<string>} One entry per cookie `value` found.\n */\nexport const cookieValues = (text: string): Array<string> => {\n const values: Array<string> = [];\n for (const objectMatch of text.matchAll(flatObjectPattern())) {\n const flatObject = objectMatch[0];\n if (!isCookieObject(flatObject)) {\n continue;\n }\n for (const valueMatch of flatObject.matchAll(cookieValuePattern())) {\n values.push(valueMatch[2] ?? \"\");\n }\n }\n return values;\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactSensitive.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/redactSensitive.ts"],"names":[],"mappings":";;;AAAA,qDAA6D;AAE7D;;;;;GAKG;AACU,QAAA,cAAc,GAAG;IAC5B,UAAU;IACV,cAAc;IACd,eAAe;IACf,aAAa;IACb,cAAc;IACd,cAAc;IACd,eAAe;IACf,SAAS;IACT,UAAU;IACV,eAAe;IACf,QAAQ;IACR,SAAS;CACV,CAAC;AAEF;;;;;;GAMG;AACU,QAAA,iBAAiB,GAAG;IAC/B,eAAe;IACf,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,cAAc;IACd,qBAAqB;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACI,MAAM,eAAe,GAAG,CAAC,OAAe,EAAU,EAAE;IACzD,IAAI,QAAQ,GAAG,OAAO,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,sBAAc,EAAE,CAAC;QACjC,yEAAyE;QACzE,sEAAsE;QACtE,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,sBAAK,GAAG,GAAG,GAAG,sBAAK,YAAY,sBAAK,QAAQ,sBAAK,GAAG,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;QACnH,uEAAuE;QACvE,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/E,CAAC;IACD,6EAA6E;IAC7E,uEAAuE;IACvE,4EAA4E;IAC5E,QAAQ,GAAG,IAAA,mCAAkB,EAAC,QAAQ,CAAC,CAAC;IACxC,KAAK,MAAM,MAAM,IAAI,yBAAiB,EAAE,CAAC;QACvC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,gBAAgB,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC;IAC1G,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAjBW,QAAA,eAAe,mBAiB1B","sourcesContent":["import { QUOTE, redactCookieValues } from \"./redactPatterns\";\n\n/**\n * Keys whose values must be redacted when they appear as JSON properties\n * (`\"key\": \"value\"`) or URL-encoded params (`key=value`). Both camelCase and\n * snake_case variants are listed because Okta uses `session_token` in its\n * redirect URL while the rest of the codebase uses `sessionToken`.\n */\nexport const SENSITIVE_KEYS = [\n \"password\",\n \"sessionToken\",\n \"session_token\",\n \"accessToken\",\n \"access_token\",\n \"refreshToken\",\n \"refresh_token\",\n \"idToken\",\n \"id_token\",\n \"authorization\",\n \"apiKey\",\n \"api_key\",\n];\n\n/**\n * HTTP header names whose values must be redacted when they appear in\n * `Header-Name: value` form (e.g. log lines printed by HTTP clients, request\n * dumps in test output). Header redaction is line-scoped because header\n * values can contain `=` and `:` characters that would otherwise confuse the\n * URL/JSON patterns.\n */\nexport const SENSITIVE_HEADERS = [\n \"authorization\",\n \"cookie\",\n \"set-cookie\",\n \"x-api-key\",\n \"x-auth-token\",\n \"proxy-authorization\",\n];\n\n/**\n * Redacts sensitive key-value pairs from a string in JSON, escaped-JSON,\n * URL-encoded, cookie, and `Header: value` formats.\n *\n * Replaces:\n * - `\"key\": \"<anything>\"` → `\"key\": \"***\"` (case-insensitive), at any JSON\n * escape depth — `\"key\":\"v\"`, `\\\"key\\\":\\\"v\\\"`, `\\\\\\\"key\\\\\\\":\\\\\\\"v\\\\\\\"`, … —\n * which covers request/response bodies and doubly-nested localStorage values\n * stored in Playwright trace entries.\n * - `\"value\": \"<anything>\"` → `\"value\": \"***\"` when the enclosing object is a\n * browser cookie (see `redactCookieValues` — property-order independent),\n * e.g. `addCookies` params carrying an Okta session cookie.\n * - `key=<anything>` → `key=***` (URL-encoded, value terminates at `&`/whitespace/`\"`)\n * - `Header-Name: <rest-of-line>` → `Header-Name: ***` (case-insensitive, line-scoped)\n *\n * @param message - The string to redact sensitive values from\n * @returns {string} The string with sensitive values replaced by `***`\n */\nexport const redactSensitive = (message: string): string => {\n let redacted = message;\n for (const key of SENSITIVE_KEYS) {\n // JSON string value at any escape depth: \"key\":\"v\" / \\\"key\\\":\\\"v\\\" / … .\n // The value is matched lazily up to the next (equally escaped) quote.\n redacted = redacted.replace(new RegExp(`(${QUOTE}${key}${QUOTE}\\\\s*:\\\\s*${QUOTE}).*?(${QUOTE})`, \"gi\"), \"$1***$2\");\n // URL-encoded: key=value (value terminates at &, whitespace, or quote)\n redacted = redacted.replace(new RegExp(`(${key}=)[^&\\\\s\"]+`, \"gi\"), \"$1***\");\n }\n // Cookie value under the generic `value` key (the cached Okta session cookie\n // replayed by `addCookies`). Handled via the shared, order-independent\n // cookie-shape helper so redaction and the structural scanner cannot drift.\n redacted = redactCookieValues(redacted);\n for (const header of SENSITIVE_HEADERS) {\n redacted = redacted.replace(new RegExp(`(^|[\\\\r\\\\n])(${header}\\\\s*:\\\\s*)[^\\\\r\\\\n]*`, \"gim\"), \"$1$2***\");\n }\n return redacted;\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactTrace.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/redactTrace.ts"],"names":[],"mappings":";;;;AAAA,2BAAiD;AACjD,0DAA0B;AAC1B,uDAAuF;AAEvF;;;;GAIG;AACH,MAAM,mBAAmB,GAA0B,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAErF,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAW,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1G;;;;;;GAMG;AACH,MAAM,iBAAiB,GAA0B;IAC/C,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,gCAAc,EAAE,GAAG,mCAAiB,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;CACzF,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,qBAAqB,GAAG,CAAC,GAAW,EAAW,EAAE;IACrD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACvD,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,gCAAgC;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,CAAC,GAAW,EAAsB,EAAE;IACzD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACI,MAAM,eAAe,GAAG,KAAK,EAAE,QAAgB,EAAiB,EAAE;IACvE,MAAM,GAAG,GAAG,IAAA,iBAAY,EAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,eAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAEvC,MAAM,QAAQ,GAAyB,EAAE,CAAC;IAC1C,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;QAC5B,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO;QACrB,QAAQ,CAAC,IAAI,CACX,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACtC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAA,iCAAe,EAAC,IAAI,CAAC,CAAC,CAAC;gBACzC,OAAO;YACT,CAAC;YACD,wEAAwE;YACxE,wEAAwE;YACxE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC7C,oEAAoE;YACpE,qEAAqE;YACrE,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC1C,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;YACtC,IAAI,OAAO,KAAK,SAAS;gBAAE,OAAO;YAClC,MAAM,QAAQ,GAAG,IAAA,iCAAe,EAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACzB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC,EAAE,CACL,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE5B,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;IACpF,IAAA,kBAAa,EAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC,CAAC;AAjCW,QAAA,eAAe,mBAiC1B","sourcesContent":["import { readFileSync, writeFileSync } from \"fs\";\nimport JSZip from \"jszip\";\nimport { redactSensitive, SENSITIVE_HEADERS, SENSITIVE_KEYS } from \"./redactSensitive\";\n\n/**\n * File suffixes inside a Playwright trace.zip whose contents are JSON Lines\n * with action/network metadata — i.e. the places where a `page.goto(url)`\n * call's URL argument is recorded verbatim.\n */\nconst TEXT_TRACE_SUFFIXES: ReadonlyArray<string> = [\".trace\", \".network\", \".stacks\"];\n\nconst isTextTraceEntry = (relPath: string): boolean => TEXT_TRACE_SUFFIXES.some(s => relPath.endsWith(s));\n\n/**\n * Lower-cased substrings whose presence is a necessary precondition for\n * `redactSensitive` to alter a buffer: it only rewrites JSON/URL matches keyed\n * on a `SENSITIVE_KEYS`/`SENSITIVE_HEADERS` name, or a cookie `value`. Keep this\n * a superset of `redactSensitive`'s triggers so the prefilter never skips an\n * entry it would change.\n */\nconst SENSITIVE_MARKERS: ReadonlyArray<string> = [\n ...new Set([...SENSITIVE_KEYS, ...SENSITIVE_HEADERS, \"value\"].map(s => s.toLowerCase())),\n];\n\n/**\n * True when `buf` may contain a value `redactSensitive` would redact, decided by\n * a case-insensitive byte scan — no UTF-8 decode. A `false` result guarantees\n * redaction is a no-op, letting the caller skip the decode/round-trip on the\n * binary bulk of a trace (screencast frames, screenshots, fonts) that dominates\n * a real `trace.zip`, with byte-identical output.\n */\nconst mightContainSensitive = (buf: Buffer): boolean => {\n const lower = Buffer.from(buf);\n for (let i = 0; i < lower.length; i++) {\n const byte = lower[i];\n if (byte !== undefined && byte >= 0x41 && byte <= 0x5a) {\n lower[i] = byte + 0x20; // ASCII upper-case → lower-case\n }\n }\n return SENSITIVE_MARKERS.some(marker => lower.includes(marker));\n};\n\n/**\n * Returns the UTF-8 decoding of `buf` only when it round-trips losslessly (the\n * re-encoded text is byte-identical to the input). This is how we tell a textual\n * resource body (a network request/response the Trace Viewer renders) from a\n * binary one (screenshot, font): a JPEG/PNG/woff decodes to replacement chars\n * and fails the round-trip, so we never re-encode — and therefore never corrupt —\n * a binary resource. Returns `undefined` for binary content.\n */\nconst asLosslessUtf8 = (buf: Buffer): string | undefined => {\n const text = buf.toString(\"utf8\");\n return Buffer.from(text, \"utf8\").equals(buf) ? text : undefined;\n};\n\n/**\n * Rewrites a Playwright trace.zip in place with sensitive values stripped.\n *\n * Two surfaces carry secrets in a retained trace:\n *\n * 1. The `.trace`/`.network`/`.stacks` JSONL streams — e.g. the `page.goto`\n * against `/auth/manager-classic#session_token=...` records the full URL as\n * the action argument, and API-call params are inlined as `jsonData`. The\n * `login()` cache-hit path also replays the cached storageState here via\n * `addCookies` (the Okta session cookie, under the generic `value` key) and\n * `addInitScript` (the `okta-token-storage` access/id/refresh tokens, nested\n * two escape levels deep) — both handled by `redactSensitive`'s cookie and\n * escape-depth-agnostic rules.\n * 2. The hashed `resources/<sha1>` entries — Playwright stores network\n * request/response *bodies* here. The `login()` slow path POSTs the account\n * password to `/api/v1/authn`, so that body — carrying the cleartext\n * password — is retained as a resource and rendered verbatim in the Trace\n * Viewer's request panel. Redacting only the streams (the previous behaviour)\n * left this copy readable.\n *\n * Streams are always redacted as text. Resource entries are redacted only when\n * they are lossless UTF-8 text (see `asLosslessUtf8`), so binary resources\n * (screenshots, fonts) are never touched. Entries with no sensitive substring\n * are left as-is to avoid needless rewrites.\n */\nexport const redactTraceFile = async (filePath: string): Promise<void> => {\n const buf = readFileSync(filePath);\n const zip = await JSZip.loadAsync(buf);\n\n const rewrites: Array<Promise<void>> = [];\n zip.forEach((relPath, file) => {\n if (file.dir) return;\n rewrites.push(\n (async () => {\n if (isTextTraceEntry(relPath)) {\n const text = await file.async(\"text\");\n zip.file(relPath, redactSensitive(text));\n return;\n }\n // Any other entry is a resource. Redact it only when it is textual, and\n // only when redaction actually changes it — binary bytes are preserved.\n const bytes = await file.async(\"nodebuffer\");\n // Skip the UTF-8 round-trip for the binary bulk that cannot carry a\n // redactable value; `redactSensitive` would be a no-op on it anyway.\n if (!mightContainSensitive(bytes)) return;\n const decoded = asLosslessUtf8(bytes);\n if (decoded === undefined) return;\n const redacted = redactSensitive(decoded);\n if (redacted !== decoded) {\n zip.file(relPath, redacted);\n }\n })()\n );\n });\n await Promise.all(rewrites);\n\n const out = await zip.generateAsync({ type: \"nodebuffer\", compression: \"DEFLATE\" });\n writeFileSync(filePath, out);\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanArtifactsForSecrets.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/scanArtifactsForSecrets.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync } from \"fs\";\nimport JSZip from \"jszip\";\nimport * as path from \"path\";\nimport { cookieValues, QUOTE, REDACTED } from \"./redactPatterns\";\nimport { SENSITIVE_KEYS } from \"./redactSensitive\";\n\n/**\n * A cleartext-secret occurrence found while scanning produced test artifacts.\n * `entry` is set when the hit is inside a zip (e.g. a Playwright `trace.zip`\n * resource body); otherwise the secret was found directly in `file`.\n */\nexport interface ArtifactSecretHit {\n readonly file: string;\n readonly entry?: string;\n readonly secret: string;\n}\n\n/**\n * Searches `buf`'s raw bytes for each secret. `Buffer.prototype.includes`\n * matches the UTF-8 bytes of the (ASCII) secret directly, so we never decode\n * the buffer to a string: binary artifacts (video, screenshots) simply yield no\n * hits, and an oversized artifact can't trip `Cannot create a string longer\n * than 0x1fffffe8 characters` and abort the scan.\n */\nconst findSecrets = (buf: Buffer, secrets: ReadonlyArray<string>): Array<string> =>\n secrets.filter(secret => secret.length > 0 && buf.includes(secret));\n\nconst scanZip = async (zipPath: string, secrets: ReadonlyArray<string>): Promise<Array<ArtifactSecretHit>> => {\n const zip = await JSZip.loadAsync(readFileSync(zipPath));\n const entries: Array<[string, JSZip.JSZipObject]> = [];\n zip.forEach((relPath, file) => {\n if (!file.dir) {\n entries.push([relPath, file]);\n }\n });\n\n const hits: Array<ArtifactSecretHit> = [];\n for (const [relPath, file] of entries) {\n for (const secret of findSecrets(await file.async(\"nodebuffer\"), secrets)) {\n hits.push({ file: zipPath, entry: relPath, secret });\n }\n }\n return hits;\n};\n\n/**\n * Recursively scans a produced-artifacts directory for cleartext `secrets`,\n * looking *inside* `trace.zip` archives (including their hashed `resources/*`\n * network-body entries) as well as plain files (logs, HAR, `error-context.md`,\n * screenshots, video). Intended as a regression guard: after redaction the\n * returned list must be empty for both passing and failing runs.\n *\n * Only catches secrets known verbatim (e.g. the account password from config).\n * For per-run-minted credentials whose value is not known in advance (the Okta\n * session cookie, `okta-token-storage` tokens) use\n * {@link scanArtifactsForUnredactedSecrets}.\n *\n * @param dir - Root artifacts directory to walk.\n * @param secrets - Cleartext values that must not appear anywhere.\n * @returns {Promise<Array<ArtifactSecretHit>>} Every occurrence found.\n */\nexport const scanArtifactsForSecrets = async (\n dir: string,\n secrets: ReadonlyArray<string>\n): Promise<Array<ArtifactSecretHit>> => {\n const hits: Array<ArtifactSecretHit> = [];\n\n const walk = async (current: string): Promise<void> => {\n // `withFileTypes` gives the entry type without a per-entry `statSync` — and\n // without following symlinks, so a dangling link can't throw and abort the\n // walk. Only descend directories and read regular files.\n for (const dirent of readdirSync(current, { withFileTypes: true })) {\n const full = path.join(current, dirent.name);\n if (dirent.isDirectory()) {\n await walk(full);\n continue;\n }\n if (!dirent.isFile()) {\n continue;\n }\n if (full.endsWith(\".zip\")) {\n hits.push(...(await scanZip(full, secrets)));\n continue;\n }\n for (const secret of findSecrets(readFileSync(full), secrets)) {\n hits.push({ file: full, secret });\n }\n }\n };\n\n await walk(dir);\n return hits;\n};\n\n/**\n * Text artifacts that carry redactable structure (trace streams, `error-context`,\n * HAR, logs) are small; media (video, screenshots) is large binary that would\n * waste work and risk the `Cannot create a string longer than 0x1fffffe8\n * characters` throw. Cap the bytes we decode for structural scanning.\n */\nconst MAX_STRUCTURAL_SCAN_BYTES = 32 * 1024 * 1024;\n\n/**\n * Decodes `buf` as UTF-8 only when it round-trips losslessly (so binary\n * resources are skipped) and is within the size cap. Returns `undefined`\n * otherwise.\n */\nconst asScannableText = (buf: Buffer): string | undefined => {\n if (buf.length > MAX_STRUCTURAL_SCAN_BYTES) {\n return undefined;\n }\n const text = buf.toString(\"utf8\");\n return Buffer.from(text, \"utf8\").equals(buf) ? text : undefined;\n};\n\n/**\n * A *structural* leak: a value that should have been redacted (it sits under a\n * sensitive JSON key, a URL-encoded sensitive param, or a browser cookie\n * `value`) but is not `***`. Needs no verbatim secret, so it catches\n * per-run-minted credentials whose value CI cannot know in advance.\n */\nexport interface StructuralSecretHit {\n readonly file: string;\n readonly entry?: string;\n readonly key: string;\n readonly kind: \"json\" | \"url\" | \"cookie\";\n}\n\nconst findStructuralLeaks = (text: string): Array<Pick<StructuralSecretHit, \"key\" | \"kind\">> => {\n const leaks: Array<Pick<StructuralSecretHit, \"key\" | \"kind\">> = [];\n for (const key of SENSITIVE_KEYS) {\n for (const match of text.matchAll(new RegExp(`${QUOTE}${key}${QUOTE}\\\\s*:\\\\s*${QUOTE}(.*?)${QUOTE}`, \"gi\"))) {\n if (match[1] !== REDACTED) {\n leaks.push({ key, kind: \"json\" });\n }\n }\n for (const match of text.matchAll(new RegExp(`${key}=([^&\\\\s\"]+)`, \"gi\"))) {\n if (match[1] !== REDACTED) {\n leaks.push({ key, kind: \"url\" });\n }\n }\n }\n // Cookie detection uses the same order-independent shape helper as redaction,\n // so a value-last cookie cannot be leaked by the redactor yet reported clean.\n for (const value of cookieValues(text)) {\n if (value !== REDACTED) {\n leaks.push({ key: \"value\", kind: \"cookie\" });\n }\n }\n return leaks;\n};\n\n/**\n * Recursively scans a produced-artifacts directory for *structural* leaks —\n * sensitive values that were not reduced to `***`. Unlike\n * {@link scanArtifactsForSecrets} it needs no verbatim secret, so it catches the\n * Okta session cookie and `okta-token-storage` tokens replayed on the `login()`\n * cache-hit path, whose values are minted per run. Intended as a CI guard\n * alongside the verbatim scan: the returned list must be empty after redaction.\n *\n * @param dir - Root artifacts directory to walk.\n * @returns {Promise<Array<StructuralSecretHit>>} Every unredacted sensitive value found.\n */\nexport const scanArtifactsForUnredactedSecrets = async (dir: string): Promise<Array<StructuralSecretHit>> => {\n const hits: Array<StructuralSecretHit> = [];\n\n const scanZipStructural = async (zipPath: string): Promise<void> => {\n const zip = await JSZip.loadAsync(readFileSync(zipPath));\n const entries: Array<[string, JSZip.JSZipObject]> = [];\n zip.forEach((relPath, file) => {\n if (!file.dir) {\n entries.push([relPath, file]);\n }\n });\n for (const [relPath, file] of entries) {\n const text = asScannableText(await file.async(\"nodebuffer\"));\n if (text === undefined) {\n continue;\n }\n for (const leak of findStructuralLeaks(text)) {\n hits.push({ file: zipPath, entry: relPath, ...leak });\n }\n }\n };\n\n const walk = async (current: string): Promise<void> => {\n for (const dirent of readdirSync(current, { withFileTypes: true })) {\n const full = path.join(current, dirent.name);\n if (dirent.isDirectory()) {\n await walk(full);\n continue;\n }\n if (!dirent.isFile()) {\n continue;\n }\n if (full.endsWith(\".zip\")) {\n await scanZipStructural(full);\n continue;\n }\n const text = asScannableText(readFileSync(full));\n if (text === undefined) {\n continue;\n }\n for (const leak of findStructuralLeaks(text)) {\n hits.push({ file: full, ...leak });\n }\n }\n };\n\n await walk(dir);\n return hits;\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setupPlugins.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/setupPlugins.ts"],"names":[],"mappings":";;;;AACA,mDAA6B;AAC7B,uEAA8D;AAI9D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,mBAAmB,GAAG,CAAC,SAAiB,EAAE,MAAc,EAAU,EAAE;IACxE,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,qBAAqB,GAAG,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;IACvE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AACrD,CAAC,CAAC;AAEF;;;;;;;GAOG;AACI,MAAM,YAAY,GAAG,CAC1B,MAAqC,EACrC,SAAoB,EACW,EAAE;IACjC,MAAM,MAAM,GAAG,IAAA,2CAAiB,GAAE,CAAC;IAEnC,+EAA+E;IAC/E,4EAA4E;IAC5E,+DAA+D;IAC/D,0EAA0E;IAC1E,MAAM,aAAa,GACjB,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;QAClC,CAAC,CAAC,mBAAmB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC;QAC/C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAEpD,MAAM,eAAe,GAAwB;QAC3C,UAAU;QACV,UAAU;QACV,MAAM;QACN,SAAS;KACV,CAAC;IAEF,MAAM,iBAAiB,GAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,eAAe,CAAC,CAAC;IAE5G,MAAM,iBAAiB,GAA+B,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAE5G,OAAO;QACL,GAAG,MAAM;QACT,QAAQ,EAAE,CAAC,GAAG,iBAAiB,EAAE,iBAAiB,CAAC;KACpD,CAAC;AACJ,CAAC,CAAC;AAhCW,QAAA,YAAY,gBAgCvB","sourcesContent":["import type { PlaywrightTestConfig, ReporterDescription } from \"@playwright/test\";\nimport * as path from \"path\";\nimport { findWorkspaceRoot } from \"./defaultPlaywrightConfig\";\nimport type { LogsReporterOptions } from \"./logsReporter\";\nimport type { Formatter } from \"./writeFileWithPrettier\";\n\n/**\n * Resolves a Playwright `outputDir` (relative or absolute) to an absolute\n * path anchored at `nxRoot`.\n *\n * Why this exists: `defaultPlaywrightConfig` intentionally returns a relative\n * `outputDir` like `../../dist/playwright/apps/<project>/test-results` so it\n * stays portable across machines and snapshot-stable for tests. Playwright\n * itself resolves that string against `rootDir` (the directory containing\n * `playwright.config.ts`) and writes screenshots/videos/traces in the right\n * place. The custom `LogsReporter` however runs as a Playwright `Reporter`\n * and calls `mkdirSync(logsFolder, { recursive: true })` directly — Node\n * resolves the relative path against `process.cwd()`, which under\n * `nx:run-commands` is the **workspace root**. The leading `../..` then\n * walks the reporter two directories *above* the workspace, scattering log\n * files (and HARs) into a sibling repo.\n *\n * The relative path Playwright emits always has the shape\n * `<../>+<workspace-relative-subpath>`, so stripping the leading `../`s and\n * anchoring the remainder at `nxRoot` yields the same absolute directory\n * Playwright would land on — regardless of how the test runner was spawned.\n */\nconst absolutiseOutputDir = (outputDir: string, nxRoot: string): string => {\n if (path.isAbsolute(outputDir)) {\n return outputDir;\n }\n const withoutLeadingDotDots = outputDir.replace(/^(?:\\.\\.[\\\\/])+/, \"\");\n return path.resolve(nxRoot, withoutLeadingDotDots);\n};\n\n/**\n * Augments a partial Playwright config with the Trackunit logs reporter and HAR folder env setup.\n * Call this in `playwright.config.ts` after `defaultPlaywrightConfig()`.\n *\n * @param config - Partial config from `defaultPlaywrightConfig()`\n * @param formatter - Prettier formatter used by `LogsReporter` to format JSON log files\n * @returns {Partial<PlaywrightTestConfig>} A new partial config with the logs reporter merged into the reporter array\n */\nexport const setupPlugins = (\n config: Partial<PlaywrightTestConfig>,\n formatter: Formatter\n): Partial<PlaywrightTestConfig> => {\n const nxRoot = findWorkspaceRoot();\n\n // Both HARs and logs derive from the resolved `outputDir`, so every Playwright\n // artifact for a run stays inside the one `dist/playwright` tree (under the\n // per-executor partition on CI). This removes the legacy fixed\n // `dist/playwright-results` location and the `hars_folders` env override.\n const outputDirBase =\n typeof config.outputDir === \"string\"\n ? absolutiseOutputDir(config.outputDir, nxRoot)\n : path.join(nxRoot, \"dist\", \"playwright\");\n const harsFolder = path.join(outputDirBase, \"hars\");\n const logsFolder = path.join(outputDirBase, \"logs\");\n\n const reporterOptions: LogsReporterOptions = {\n logsFolder,\n harsFolder,\n nxRoot,\n formatter,\n };\n\n const logsReporterEntry: ReporterDescription = [path.resolve(__dirname, \"./logsReporter\"), reporterOptions];\n\n const existingReporters: Array<ReporterDescription> = Array.isArray(config.reporter) ? config.reporter : [];\n\n return {\n ...config,\n reporter: [...existingReporters, logsReporterEntry],\n };\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeFileWithPrettier.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/plugins/writeFileWithPrettier.ts"],"names":[],"mappings":";;;;AAAA,uCAAoC;AACpC,2BAAqD;AACrD,mDAA6B;AAQ7B;;;;;;;;;GASG;AACI,MAAM,qBAAqB,GAAG,KAAK,EACxC,MAAc,EACd,QAAgB,EAChB,OAAe,EACf,MAAiB,EACjB,eAAiC,EAAE,QAAQ,EAAE,OAAO,EAAE,EACvC,EAAE;IACjB,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;QAC3E,eAAM,CAAC,IAAI,CAAC,0BAA0B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,GAAG,YAAY,CAAC;IAChC,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACxD,IAAA,kBAAa,EAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AACnD,CAAC,CAAC;AAvBW,QAAA,qBAAqB,yBAuBhC","sourcesContent":["import { logger } from \"@nx/devkit\";\nimport { WriteFileOptions, writeFileSync } from \"fs\";\nimport * as path from \"path\";\nimport { format, resolveConfig } from \"prettier\";\n\nexport type Formatter = {\n resolveConfig: typeof resolveConfig;\n format: typeof format;\n};\n\n/**\n * Writes a file with Prettier formatting applied.\n * Automatically detects parser based on file extension.\n *\n * @param nxRoot - Absolute path to the NX workspace root (used to locate `prettier.config.mjs`)\n * @param filePath - Destination file path\n * @param content - Raw file content to format and write\n * @param writer - Prettier formatter instance\n * @param writeOptions - Node.js `writeFileSync` options (default: UTF-8)\n */\nexport const writeFileWithPrettier = async (\n nxRoot: string,\n filePath: string,\n content: string,\n writer: Formatter,\n writeOptions: WriteFileOptions = { encoding: \"utf-8\" }\n): Promise<void> => {\n const prettierConfigPath = path.join(nxRoot, \"prettier.config.mjs\");\n const options = await writer.resolveConfig(prettierConfigPath).catch(error => {\n logger.info(`Prettier config error: ${String(error)}`);\n return undefined;\n });\n if (!options) {\n throw new Error(\"Could not find prettier config\");\n }\n\n if (filePath.endsWith(\"json\")) {\n options.parser = \"json\";\n } else {\n options.parser = \"typescript\";\n }\n const prettySrc = await writer.format(content, options);\n writeFileSync(filePath, prettySrc, writeOptions);\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"support.js","sourceRoot":"","sources":["../../../../../libs/iris-app-sdk/iris-app-playwright/src/support.ts"],"names":[],"mappings":";;;AAAA,8DAAoC;AACpC,8DAAoC","sourcesContent":["export * from \"./fixtures/baseTest\";\nexport * from \"./fixtures/describe\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Codeowner.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/utils/Codeowner.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Parses lines from a CODEOWNERS-style file into a mapping of patterns → owners.\n * Skips blank lines and comments (lines beginning with `#`).\n *\n * @param {string[]} lines - Each element is a line from your CODEOWNERS file.\n * @returns {Record<string, string>} An object whose keys are normalized path patterns and values are the owner handle.\n */\nexport const parseCodeowners = (lines: Array<string>): Record<string, string> => {\n const patterns: Record<string, string> = {};\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n const [pattern, owner] = trimmed.split(/\\s+/, 2);\n if (pattern && owner) {\n const normalizedPattern = pattern.replace(/^\\/+|\\/+$/g, \"\");\n patterns[normalizedPattern] = owner;\n }\n }\n return patterns;\n};\n\n/**\n * Converts a full team handle (potentially namespaced and with platform suffix)\n * into its \"short\" team name.\n *\n * @param {string} teamName - e.g. `\"@trackunit/backend-be\"` or `\"@trackunit/frontend-fe\"`\n * @returns {string | undefined} e.g. `\"backend\"` or `\"frontend\"`, or `undefined` if no name given.\n */\nexport const toShortTeamName = (teamName?: string): string | undefined => {\n if (!teamName) {\n return undefined;\n }\n let shortName = teamName;\n if (teamName.startsWith(\"@\")) {\n shortName = shortName.slice(shortName.indexOf(\"/\") + 1);\n }\n if (shortName.endsWith(\"-be\") || shortName.endsWith(\"-fe\")) {\n shortName = shortName.slice(0, shortName.lastIndexOf(\"-\"));\n }\n return shortName;\n};\n\n/**\n * Recursively looks up the CODEOWNER for a given file or directory path\n * by reading your workspace's CODEOWNERS file.\n *\n * @param {string} currentPath - Absolute path to the file/directory you're querying.\n * @param {string} workspaceRoot - Absolute path to your repo/workspace root.\n * @param {string} [codeownersFileName=\"TEAM_CODEOWNERS\"] - Filename to read at the root.\n * @returns {string|undefined} The owner handle or `undefined` if none found.\n */\nexport const getCodeowner = (\n currentPath: string,\n workspaceRoot: string,\n codeownersFileName = \"TEAM_CODEOWNERS\"\n): string | undefined => {\n if (!workspaceRoot) {\n return undefined;\n }\n\n const codeownersPath = path.join(workspaceRoot, codeownersFileName);\n if (!fs.existsSync(codeownersPath)) {\n return undefined;\n }\n\n const codeownersLines = fs.readFileSync(codeownersPath, \"utf8\").split(\"\\n\");\n const codeowners = parseCodeowners(codeownersLines);\n\n let relPath = path\n .relative(workspaceRoot, currentPath)\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+|\\/+$/g, \"\");\n\n let parent = path.posix.dirname(relPath);\n while (relPath !== parent) {\n const codeowner = codeowners[relPath];\n if (codeowner) {\n return codeowner;\n }\n if (parent === \".\") {\n break;\n }\n relPath = parent;\n parent = path.posix.dirname(relPath);\n }\n\n return codeowners[\".\"];\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileNameBuilder.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/utils/fileNameBuilder.ts"],"names":[],"mappings":";;AAOA,kDAMC;AAWD,0CAGC;AA3BD;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,GAAW;IAC7C,OAAO,GAAG;SACP,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC;SACxC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,QAAgB,EAAE,KAAa,EAAE,YAAqB;IACpF,MAAM,QAAQ,GAAG,GAAG,QAAQ,IAAI,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;IAC7H,OAAO,mBAAmB,CAAC,QAAQ,CAAC,CAAC;AACvC,CAAC","sourcesContent":["/**\n * Sanitizes a string to be safe for use in filenames.\n * Removes special characters and normalizes whitespace and hyphens.\n *\n * @param str - The string to sanitize (e.g., test suite or test name)\n * @returns {string} Sanitized string safe for filesystem use\n */\nexport function sanitizeForFilename(str: string): string {\n return str\n .replace(/[^a-zA-Z0-9-_\\s\\[\\]\\(\\)]/g, \"\")\n .replace(/-+/g, \"-\")\n .replace(/\\s+/g, \" \")\n .replace(/^-|-$/g, \"\");\n}\n\n/**\n * Builds a standardized filename for test artifacts (e.g., screenshots, traces).\n * Combines test name, state, and attempt number into a readable filename.\n *\n * @param testName - The name of the test (will be sanitized)\n * @param state - The test state (e.g., 'passed', 'failed')\n * @param currentRetry - The current retry attempt number (0-based)\n * @returns {string} A sanitized filename in the format: \"testName (state) (attempt N)\"\n */\nexport function fileNameBuilder(testName: string, state: string, currentRetry?: number): string {\n const fileName = `${testName} ${currentRetry !== undefined ? `(Attempt ${currentRetry + 1})` : \"\"} (${state.toLowerCase()})`;\n return sanitizeForFilename(fileName);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileUpdater.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-playwright/src/utils/fileUpdater.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACI,MAAM,gBAAgB,GAAG,CAAC,IAAU,EAAE,IAAY,EAAE,OAAwC,EAAQ,EAAE;IAC3G,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAEpC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC,CAAC;AAdW,QAAA,gBAAgB,oBAc3B;AAEF;;;;;GAKG;AACI,MAAM,gBAAgB,GAAG,CAAC,IAAU,EAAE,IAAY,EAAQ,EAAE;IACjE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACpB,CAAC,CAAC;AANW,QAAA,gBAAgB,oBAM3B","sourcesContent":["import { Tree } from \"@nx/devkit\";\n\n/**\n * Reads a file from an NX Tree, applies an updater function, and writes it back if changed.\n *\n * @param tree - File system tree\n * @param path - Path to source file in the Tree\n * @param updater - Function that maps the current file content to new content\n */\nexport const updateFileInTree = (tree: Tree, path: string, updater: (fileContent: string) => string): void => {\n if (!tree.exists(path)) {\n throw new Error(`File not found: ${path}`);\n }\n\n const fileContent = tree.read(path, \"utf-8\");\n if (!fileContent) {\n throw new Error(`File is empty: ${path}`);\n }\n const result = updater(fileContent);\n\n if (result !== fileContent) {\n tree.write(path, result);\n }\n};\n\n/**\n * Deletes a file from an NX Tree. No-op if the file does not exist.\n *\n * @param tree - File system tree\n * @param path - Path to source file in the Tree\n */\nexport const deleteFileInTree = (tree: Tree, path: string): void => {\n if (!tree.exists(path)) {\n return;\n }\n\n tree.delete(path);\n};\n"]}
|