Skip to content

SPARK-843491: Remediate 8 High security findings in components - #862

Draft
mkesavan13 wants to merge 1 commit into
masterfrom
SPARK-843491-remediate-8-security-findings
Draft

SPARK-843491: Remediate 8 High security findings in components#862
mkesavan13 wants to merge 1 commit into
masterfrom
SPARK-843491-remediate-8-security-findings

Conversation

@mkesavan13

@mkesavan13 mkesavan13 commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

COMPLETES #SPARK-843491

This pull request addresses

Remediates 8 unique High-severity security findings (UF-001 through UF-008) identified in the webex/components repository via Codeguard and workflow security harness scans (scan date: 2026-07-06, commit: b4f087a). Findings span client-side trust boundaries: URL validation, OpenUrl sink, OAuth CSRF state + token storage, prototype pollution, markdown HTML sanitization, and CI supply chain.

Root Cause (per finding):

  • UF-001 — isValidUrl (src/util.js) checked only the URL protocol; no host/loopback allow-list was enforced for http:/https: URLs.
  • UF-002 — window.open(data.url, '_blank') in useActionOpenUrl.js lacked the noopener,noreferrer feature string.
  • UF-003 — OAuth CSRF state generated from only 32 bits (Uint8Array(4)) and never stored before opening the auth window.
  • UF-004 — OAuth cookie written with only the secure flag; SameSite=Strict was absent.
  • UF-005 — sessionStorage/localStorage stored the raw access token; the destructured ttl value was never applied.
  • UF-006 — deepMerge in src/util.js iterated Object.entries(src) with no guard against __proto__, constructor, or prototype keys.
  • UF-007 — markdownIt.render() output was injected via dangerouslySetInnerHTML with no HTML sanitizer.
  • UF-008 — Both CI configurations used npm install (non-deterministic) without an audit gate before release/build steps.

by making the following changes

  • src/util.jsisValidUrl: adds isBlockedHost helper that blocks loopback (localhost, 127.x, [::1]), link-local (169.254.x, fe80::), and private/unique-local ranges for http:/https: URLs; data: URIs are unaffected. deepMerge: adds FORBIDDEN_MERGE_KEYS set (__proto__, constructor, prototype) to skip those keys.
  • src/components/adaptive-cards/hooks/useActionOpenUrl.js — Passes 'noopener,noreferrer' to window.open.
  • src/components/SignIn/SignIn.jsx — CSRF state: 16-byte crypto.getRandomValues → hex string, stored in csrfStateRef before auth window opens. Cookie: adds SameSite=Strict. Session/local storage: stores {token, expiry} JSON envelope with TTL instead of raw token.
  • src/components/adaptive-cards/Markdown/Markdown.jsx — Wraps markdownIt.render() output with DOMPurify.sanitize before dangerouslySetInnerHTML.
  • package.json — Adds dompurify ^3.4.13 as a runtime dependency.
  • .circleci/config.yml — Replaces npm install with npm ci; adds npm audit --audit-level=high step gating all downstream jobs.
  • .github/workflows/npm-storybook-release.yml — Replaces npm install with npm ci; adds npm audit --audit-level=high step gating the npx semantic-release step that holds NPM_TOKEN/GITHUB_TOKEN.
  • ai-docs/SECURITY.md — Updates Input Validation posture to document isValidUrl host blocking and DOMPurify layer (spec-currency).
  • src/components/ai-docs/components-spec.md — Updates SignIn and Markdown entries for CSRF state generation/storage and DOMPurify (spec-currency).

New test files: src/util.test.js, src/components/SignIn/SignIn.test.jsx, src/components/adaptive-cards/Markdown/Markdown.test.jsx, src/components/adaptive-cards/hooks/useActionOpenUrl.test.js.

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

  • The testing is done with the amplify link

  • Unit tests added covering all independently testable findings (AC-1 through AC-7, 32 targeted tests across 4 new test files).

  • Gate 1 (compile: npm run build): PASSED — ESM and UMD bundles built successfully.

  • Gate 2 (unit-test: NODE_ENV=test npm run test): PASSED — 262 tests, 18 suites, 107 snapshots all passing.

  • Gate 3: Not run.

Testing

  • Tests added: 32 (across 4 new co-located test files)
  • Workflow verification: Gate 1 (compile npm run build) passed; Gate 2 (unit-test NODE_ENV=test npm run test) passed — 262 tests, 18 suites, 107 snapshots; Gate 3 not run.

Acceptance Criteria

ID Criterion Source JiraToPr status Evidence
AC-1 isValidUrl blocks loopback, link-local, and private-range hosts for http:/https: URLs. Jira description: UF-001 Unit validated 11 cases in src/util.test.js — all passing in Gate 2
AC-2 Action.OpenUrl opens with noopener,noreferrer and rejects disallowed hosts/schemes. Jira description: UF-002 Unit validated 4 cases in useActionOpenUrl.test.js — all passing in Gate 2
AC-3 OAuth CSRF state uses ≥16 bytes of entropy, hex-serialized, stored before auth window opens (client-owned). Jira description: UF-003 Unit validated (AC-3a) + External validation required (AC-3b) 3 cases in SignIn.test.jsx — all passing in Gate 2
AC-4 Cookie token written with SameSite=Strict alongside secure flag. Jira description: UF-004 Unit validated 1 case in SignIn.test.jsx — passing in Gate 2
AC-5 Session/local storage stores {token, expiry} envelope using ttl; no raw unbounded token write. Jira description: UF-005 Unit validated 2 cases in SignIn.test.jsx — passing in Gate 2
AC-6 deepMerge skips __proto__, constructor, and prototype keys. Jira description: UF-006 Unit validated 5 cases in src/util.test.js — all passing in Gate 2
AC-7 markdownIt.render() output is wrapped with DOMPurify.sanitize before dangerouslySetInnerHTML. Jira description: UF-007 Unit validated 6 cases in Markdown.test.jsx — all passing in Gate 2
AC-8 Both CI configs use npm ci with npm audit --audit-level=high as a required gate. Jira description: UF-008 Config-inspection verified + External validation required .circleci/config.yml and .github/workflows/npm-storybook-release.yml inspected — release-blocking enforcement is CI-owned

External Validation Required

  • AC-3 — OAuth CSRF state is generated with >=16 bytes of entropy, serialized as hex/base64url, stored, and validated so mismatched or missing state on return is rejected.

    • Reason: external-dependency
    • Details: Generation/serialization/storage of >=16-byte state is unit-provable (UT-3). Returned-state rejection is host-owned: SignIn.jsx:53-112 opens a cross-origin popup, polls newWindow.closed, and calls caller getAccessToken() with no postMessage/location reader, so no in-repo test drives it. Validator: app team owning redirectUri compares returned state to the stored value and aborts on mismatch/absence. Uncertainty: JiraToPr cannot enforce the host-side check.
    • Source: UF-003 finding + Acceptance Criteria (CSRF state generation meets remediation).
    • Observable pass condition: A callback carrying a state that does not match the stored value (or carrying no state) does not complete sign-in; a matching state proceeds normally.
  • AC-8 — Both CI configurations use 'npm ci' for deterministic installs and enforce an 'npm audit --audit-level=high' gate that blocks the release on high-severity findings.

    • Reason: ci-pipeline
    • Details: npm ci + npm audit --audit-level=high content is inspectable via UT-8 (.circleci/config.yml) and UT-9 (release workflow), but these are config-inspection targets, not jest unit tests, and the release-blocking guarantee only manifests when CI runs. Validator: release owners run both pipelines; a high-severity audit finding must fail before the token-holding release. Uncertainty: JiraToPr cannot run CI; validate against the existing lockfile first.
    • Source: UF-008 finding + Acceptance Criteria (CI uses npm ci with a required audit gate).

Contract Discovery Warnings

  • Manifest reference discovery capped at 100 strings.

AI Assistance

  • Code was generated entirely by GAI
  • Tool: Other - JiraToPr automated remediation workflow (Claude Sonnet 4.6)
  • This PR is related to
    • Defect fix

Checklist before merging

  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the testing document

Jira: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-843491

@mkesavan13 mkesavan13 added the jira-to-pr Automated PR created by JiraToPr workflow label Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira-to-pr Automated PR created by JiraToPr workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant