From 00168e009e1de38acac360a0bfe64569b8c03ae8 Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Mon, 27 Apr 2026 03:05:52 +0000 Subject: [PATCH] =?UTF-8?q?M-029=20Pass=203=20batch=20B:=20T-1=20tests=20f?= =?UTF-8?q?or=204=20detail=20pages=20=E2=80=94=20XSS=20hardening?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continues Pass 3. Each detail page has its own narrow attack surface (subject DN, last_test_message, error_message) that the test exercises with literal '; + +const xssCert = { + id: 'mc-xss-001', + name: xssPayload, + common_name: xssPayload, + sans: [xssPayload, 'plain.example.com'], + status: 'Active', + environment: xssPayload, + issuer_id: 'iss-xss', + certificate_profile_id: 'cp-xss', + owner_id: 'o-xss', + team_id: 't-xss', + renewal_policy_id: 'rp-xss', + expires_at: new Date(Date.now() + 30 * 86400000).toISOString(), + created_at: new Date().toISOString(), + not_before: new Date(Date.now() - 86400000).toISOString(), + not_after: new Date(Date.now() + 30 * 86400000).toISOString(), + serial_number: xssPayload, + fingerprint_sha256: xssPayload, + pem_encoded: xssPayload, +}; + +describe('CertificateDetailPage — render + XSS hardening (M-026 / M-029 Pass 3)', () => { + beforeEach(() => { + vi.clearAllMocks(); + cleanup(); + delete (window as unknown as { __xss_pwned__?: number }).__xss_pwned__; + + // Wire defaults for the sidecar queries — every page render fans out + // 7+ queries and any unmocked call will reject and surface an error + // boundary instead of the page body. + vi.mocked(client.getCertificate).mockResolvedValue(xssCert as never); + vi.mocked(client.getCertificateVersions).mockResolvedValue([] as never); + vi.mocked(client.getTargets).mockResolvedValue({ data: [], total: 0, page: 1, per_page: 50 } as never); + vi.mocked(client.getProfile).mockResolvedValue({ id: 'cp-xss', name: 'Profile' } as never); + vi.mocked(client.getProfiles).mockResolvedValue({ data: [], total: 0, page: 1, per_page: 50 } as never); + vi.mocked(client.getRenewalPolicies).mockResolvedValue({ data: [], total: 0, page: 1, per_page: 500 } as never); + vi.mocked(client.getJobs).mockResolvedValue({ data: [], total: 0, page: 1, per_page: 50 } as never); + }); + + it('renders the page when getCertificate resolves', async () => { + vi.mocked(client.getCertificate).mockResolvedValue({ ...xssCert, common_name: 'plain.example.com' } as never); + renderRoute(); + await waitFor(() => { + expect(screen.getByText('plain.example.com')).toBeInTheDocument(); + }); + }); + + it('does NOT execute '; + +const xssIssuer = { + id: 'iss-xss-001', + name: xssPayload, + type: xssPayload, + enabled: true, + config: { acme_directory_url: xssPayload, eab_kid: xssPayload }, + last_tested_at: new Date().toISOString(), + last_test_status: 'failed', + last_test_message: xssPayload, +}; + +describe('IssuerDetailPage — render + XSS hardening (M-026 / M-029 Pass 3)', () => { + beforeEach(() => { + vi.clearAllMocks(); + cleanup(); + delete (window as unknown as { __xss_pwned__?: number }).__xss_pwned__; + }); + + it('renders the page header when getIssuer resolves', async () => { + vi.mocked(client.getIssuer).mockResolvedValue({ ...xssIssuer, name: 'Plain Name', type: 'acme' } as never); + vi.mocked(client.getCertificates).mockResolvedValue({ data: [], total: 0, page: 1, per_page: 50 } as never); + renderRoute(); + await waitFor(() => { + expect(screen.getByText('Plain Name')).toBeInTheDocument(); + }); + }); + + it('does NOT execute '; + +const xssJob = { + id: 'j-xss-001', + type: xssPayload, + status: 'Failed', + certificate_id: 'mc-xss', + agent_id: 'a-xss', + error_message: xssPayload, + details: { reason: xssPayload }, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), +}; + +describe('JobDetailPage — render + XSS hardening (M-026 / M-029 Pass 3)', () => { + beforeEach(() => { + vi.clearAllMocks(); + cleanup(); + delete (window as unknown as { __xss_pwned__?: number }).__xss_pwned__; + }); + + it('renders the page when getJob resolves', async () => { + vi.mocked(client.getJob).mockResolvedValue({ ...xssJob, type: 'renewal', error_message: '' } as never); + vi.mocked(client.getJobVerification).mockResolvedValue(null as never); + vi.mocked(client.getAuditEvents).mockResolvedValue({ data: [], total: 0, page: 1, per_page: 10 } as never); + renderRoute(); + await waitFor(() => { + expect(screen.getByText(/j-xss-001/)).toBeInTheDocument(); + }); + }); + + it('does NOT execute '; + +const xssTarget = { + id: 't-xss-001', + name: xssPayload, + type: 'nginx', + agent_id: 'a-xss', + config: { host: xssPayload, path: xssPayload }, + last_tested_at: new Date().toISOString(), + last_test_status: 'failed', + last_test_message: xssPayload, +}; + +describe('TargetDetailPage — render + XSS hardening (M-026 / M-029 Pass 3)', () => { + beforeEach(() => { + vi.clearAllMocks(); + cleanup(); + delete (window as unknown as { __xss_pwned__?: number }).__xss_pwned__; + }); + + it('renders the page header when getTarget resolves', async () => { + vi.mocked(client.getTarget).mockResolvedValue({ ...xssTarget, name: 'Plain Name' } as never); + vi.mocked(client.getJobs).mockResolvedValue({ data: [], total: 0, page: 1, per_page: 50 } as never); + renderRoute(); + await waitFor(() => { + expect(screen.getByText('Plain Name')).toBeInTheDocument(); + }); + }); + + it('does NOT execute