Skip to content

feat(web): add category tabs, validation, and paste support#62

Merged
danielewood merged 8 commits intomainfrom
feat/wasm-tabs
Feb 24, 2026
Merged

feat(web): add category tabs, validation, and paste support#62
danielewood merged 8 commits intomainfrom
feat/wasm-tabs

Conversation

@danielewood
Copy link
Collaborator

@danielewood danielewood commented Feb 24, 2026

Summary

  • Category tabs: Replace Inspect/Verify tab navigation with unified category tabs (Leaf, Intermediate, Root, Keys) — certificates are organized by type with click-to-expand detail rows showing validation checks and metadata
  • WASM validation: Add certkitValidateCert function that runs expiration, key strength, signature algorithm, and trust chain checks against the Mozilla root store — results displayed inline in expandable detail rows
  • Concurrent AIA resolution: Parallelize AIA fetches with configurable concurrency (default 20, WASM uses 50) using a context-cancellable semaphore pattern
  • Paste support: Ctrl+V / Cmd+V pastes PEM or certificate text directly into the drop zone without needing a file (1 MB limit)
  • Hardening: Processing guard prevents concurrent operations, XSS escaping on dynamic content, panic fix for empty Organization slices, file-read error handling, reset recovery

Test plan

  • go test -race ./... passes
  • GOOS=js GOARCH=wasm go vet ./cmd/wasm/ && go build -o /dev/null ./cmd/wasm/
  • cd web && npm test passes
  • Manual: drop cert files, verify category tabs show correct grouping
  • Manual: click a certificate row, verify validation checks expand inline
  • Manual: paste a PEM certificate (Ctrl+V), verify it processes
  • Manual: paste into the password input field, verify it does NOT trigger processing
  • Manual: verify AIA progress bar during chain resolution

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings February 24, 2026 00:10
danielewood and others added 4 commits February 23, 2026 19:12
AIA fetches within each depth round now run concurrently using a
goroutine pool with semaphore. Reduces wall-clock time from ~30s to ~4s
for stores with many leaf certificates. MemStore mutations remain
sequential — no synchronization changes needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow users to paste PEM/certificate text directly into the page
(Ctrl+V / Cmd+V) instead of requiring file drag-and-drop. Paste
events on the document are intercepted, converted to bytes via
TextEncoder, and fed through the same certkitAddFiles pipeline as
file drops. Input fields (password box) are excluded so normal
paste still works there.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix index-out-of-bounds panic in checkTrustChain when certificate has
  empty CommonName and empty Organization (validate.go:256)
- Add context cancellation check on AIA semaphore acquire to prevent
  goroutines blocking after context timeout (aia.go)
- Extract shared addFileObjects helper to eliminate DRY violation between
  processFiles and processPasted
- Show user-visible error when all pasted/dropped inputs fail to parse
- Add 1 MB paste size limit to prevent browser memory exhaustion
- Add processing guard to prevent concurrent paste+drop operations
- Show feedback when pasting while WASM is still loading
- Escape cert.cert_type in key detail badge HTML for defense-in-depth
- Remove dead "Serial" check in buildMetadataHTML
- Remove dead visibleCertSKIs state variable
- Add commit refs to all changelog entries (CL-3/CL-4)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reset button clears `processing` flag as escape hatch for stuck state
  (e.g., WASM crash leaving a hung promise)
- Wrap processFiles arrayBuffer calls in try/catch so file-read errors
  show a user-visible status instead of being silently swallowed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the web UI with category-based certificate organization, browser-based validation using Mozilla root store, concurrent AIA resolution with progress tracking, and paste support for certificate data.

Changes:

  • Replace tab-based navigation with category tabs (Leaf, Intermediate, Root, Keys) and click-to-expand validation details
  • Add WASM validation function that performs expiration, key strength, signature, and trust chain checks
  • Parallelize AIA fetches (default 20, WASM uses 50 concurrent requests) with progress reporting
  • Enable Ctrl+V/Cmd+V paste support for certificate data (1 MB limit)

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
web/public/index.html Adds category tab navigation, progress bar UI, removes old filter checkboxes, restructures table headers
web/public/app.js Implements category tabs, paste handler, detail row expansion with validation, progress callbacks, processing guards
internal/certstore/aia.go Adds concurrent AIA fetching with semaphore, progress callbacks, Mozilla root checks
cmd/wasm/validate.go New validation function with expiration, key strength, signature, and trust chain checks
cmd/wasm/aia.go Integrates progress callback to JavaScript via setTimeout
CHANGELOG.md Documents new features with commit references

@claude

This comment has been minimized.

@claude

This comment has been minimized.

Include previously uncommitted main.go (certkitValidateCert registration,
serial/subject/EKU fields, formatDN/formatEKUs helpers) and style.css
(category tabs, validation detail rows, metadata grid, clickable rows).

Address CLAUDE.md violations in validate.go:
- ERR-6: return MozillaRootPool error immediately instead of logging
- CS-5: use checkTrustChainInput struct for checkTrustChain (>2 params)
- CC-2: bind validation goroutine to context with 30s timeout

Add ARIA accessibility:
- role="tablist" on category nav, role="tab" + aria-selected on buttons
- role="progressbar" with aria-valuemin/max/now on AIA progress bar
- Dynamic aria-valuenow updates in JS progress callback and reset helpers

Dismiss Copilot wg.Go() false positive — method exists in Go 1.25+.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude

This comment has been minimized.

Update TestResolveAIA_CancelledContext to not assert fetchCalled: the
context-cancellable semaphore select may short-circuit before reaching
the fetcher, making the assertion non-deterministic. Warnings are
produced either way.

Fix prettier formatting on style.css.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 24, 2026 02:14
Replace stale mirrors-prettier (v3.1.0, last stable release) with a
local hook running npx prettier, matching CI's npx prettier@latest
(3.8.1). The version mismatch caused formatting drift between local
pre-commit and CI checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.

…etail rows

- Thread single `now` into checkTrustChainInput to avoid dual time.Now()
  calls producing inconsistent results near expiry boundaries
- Hoist extKeyUsageNames map to package-level var (avoid per-call alloc)
- Guard certRowClick against out-of-order async resolution (check
  selectedDetailSKI and tr.isConnected after await)
- Reset aria-selected alongside class toggle in reset handler
- Use fmt.Errorf %w wrapping for JSON marshal error in validate

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@danielewood danielewood merged commit 392878a into main Feb 24, 2026
29 checks passed
@danielewood danielewood deleted the feat/wasm-tabs branch February 24, 2026 02:25
@claude
Copy link

claude bot commented Feb 24, 2026

test comment

@claude
Copy link

claude bot commented Feb 24, 2026

Removing test comment posted earlier.

@claude
Copy link

claude bot commented Feb 24, 2026

Code review

Found 4 issues: 1 bug and 3 CLAUDE.md violations. Inline comments follow in separate messages since the review API requires approval in this environment.

Bug (internal/certstore/aia.go:109): progressTotal overflow in multi-depth AIA resolution

progressTotal is computed once before the depth loop by counting only the certs currently in the store that satisfy needsResolution. But newly-fetched intermediates are added to the store during Phase 3 of each round and can themselves satisfy needsResolution in a subsequent round, causing them to be added to processed too.

Concrete trace with a leaf -> intermediate -> root chain where only the leaf was uploaded:

  • Initial: progressTotal = 1 (only the leaf counts)
  • Round 0: leaf fetches its intermediate, Phase 3 adds it to the store, leaf is marked processed -> OnProgress(1, 1) = 100%
  • Round 1: the newly-added intermediate needs its issuer, gets processed -> OnProgress(2, 1) = 200%

The final tick at line 244 resets the bar to 100% at the end, but the mid-loop call with completed > total already causes progressFill.style.width = 200% in the browser. This is not a theoretical edge case -- it is the normal multi-depth resolution path.

Fix: recompute progressTotal at the start of each round (e.g. progressTotal += len(queue) before Phase 1), or cap the reported value with min(len(processed), progressTotal) in the OnProgress call.

@claude
Copy link

claude bot commented Feb 24, 2026

CLAUDE.md CTX-2 violation (cmd/wasm/validate.go:79): context discarded

runValidation accepts context.Context as its first parameter but immediately discards it. The caller at line 41 establishes a 30-second timeout that is never honored:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// ...
result, err := runValidation(ctx, globalStore, ski)  // ctx silently dropped inside

CTX-2 (MUST): Propagate non-nil ctx; honor Done/deadlines/timeouts.

If the user navigates away or the browser tab is closed, runValidation continues running to completion regardless of the timeout. The context should be propagated to its callees, or -- if propagation is genuinely impossible in the WASM context -- the parameter should be removed and the timeout enforced at a different boundary.

@claude
Copy link

claude bot commented Feb 24, 2026

CLAUDE.md CL-3 / CL-4 violation (CHANGELOG.md:12): commit refs do not exist in this repository

The refs d8b9759 and 837e5e8 used in lines 12-19 and the corresponding link definitions added at lines 576-577 do not match any commit in this PR or anywhere in the repository history. These appear to be stale SHAs from a previous branch iteration before a rebase or squash.

The actual PR commits are: 4e8b1a0, f544847, 607edcb, 2fb7a63, 3feac67, 33c9588, b9430db, eb372e0.

CL-3 (MUST): each entry must end with the commit ref that introduced the change. CL-4 (MUST): the link definition at the bottom must point to the actual commit. Update all five entries and both link definitions to the correct SHAs.

@claude
Copy link

claude bot commented Feb 24, 2026

CLAUDE.md T-9 violation (cmd/wasm/validate.go:131): certkit-specific policy logic has no tests

checkExpiration, checkKeyStrength, checkSignature, and checkTrustChain implement certkit certificate validation policy -- thresholds like RSA < 2048 = fail, MD5/SHA1 = fail/warn, and the expired-cert time-travel heuristic -- that are certkit-specific decisions, not thin stdlib wrappers. T-9 (MUST): Test certkit logic, not upstream behavior.

The //go:build js && wasm constraint prevents direct unit-testing of these functions. The standard remedy is to extract the pure-Go policy logic into a non-WASM internal package that go test ./... can reach, leaving only the thin WASM wrappers in cmd/wasm. The no-tests exemption in CLAUDE.md section 8 covers cmd/certkit specifically -- not cmd/wasm.

danielewood added a commit that referenced this pull request Feb 24, 2026
Extract validation policy logic from cmd/wasm/validate.go into
internal/certstore/validate.go — removes //go:build constraint so
checks are reachable by go test. WASM file is now a thin JS wrapper.

Add tests for CheckExpiration, CheckKeyStrength, CheckSignature, and
CheckTrustChain covering policy thresholds (RSA <2048 = fail,
MD5/SHA1 = fail/warn, expired/not-yet-valid, nil roots, self-signed).

Fix progressTotal overflow in AIA resolution: recompute total each
depth round to include newly-discovered intermediates, preventing
completed from exceeding total mid-loop.

Propagate context through RunValidation with ctx.Err() check between
root pool load and validation checks, honoring the 30s WASM timeout.

Fix stale changelog refs: d8b9759/837e5e8 (pre-rebase) → 392878a
(squash merge SHA).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
danielewood added a commit that referenced this pull request Feb 24, 2026
* fix: address post-merge review findings from PR #62

Extract validation policy logic from cmd/wasm/validate.go into
internal/certstore/validate.go — removes //go:build constraint so
checks are reachable by go test. WASM file is now a thin JS wrapper.

Add tests for CheckExpiration, CheckKeyStrength, CheckSignature, and
CheckTrustChain covering policy thresholds (RSA <2048 = fail,
MD5/SHA1 = fail/warn, expired/not-yet-valid, nil roots, self-signed).

Fix progressTotal overflow in AIA resolution: recompute total each
depth round to include newly-discovered intermediates, preventing
completed from exceeding total mid-loop.

Propagate context through RunValidation with ctx.Err() check between
root pool load and validation checks, honoring the 30s WASM timeout.

Fix stale changelog refs: d8b9759/837e5e8 (pre-rebase) → 392878a
(squash merge SHA).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: simplify substring matching in validation tests

Replace custom containsSubstring/findSubstring helpers with
strings.Contains from the standard library.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants