mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 12:21:31 +00:00
fix(web): Hotfix #8 — L-015 line-grep guard + CodeQL formatStatus orphan
Two separate issues caught after Phase 5 push: ═════════════════════════ ISSUE 1: L-015 CI GUARD ═════════════════════════ The Frontend Build job on commit868f1c25(sidebar maintainer attribution) failed with: ::error::L-015 regression: target="_blank" without rel="noopener noreferrer": web/src/components/Layout.tsx:297: target="_blank" Root cause: the bundle-8-L-015-target-blank-rel-noopener.sh guard uses LINE-BASED grep — it greps each line for `target="_blank"` then filters lines containing `noopener noreferrer`. My sidebar attribution split those across two lines (target= on 297, rel= on 298), so the line with target= never had noopener visible to the line-grep filter and the guard fired. Worth noting: a Haiku-generated recommendation on the failing run claimed "the code already has the correct rel attribute, re-run the CI job." That recommendation was wrong — I verified the failure reproduces locally. Haiku also invented a "FormField React.Children.only" error that doesn't exist (all 7 FormField tests pass locally). Ignored both. Fix: migrate the sidebar attribution from a bare <a target="_blank"> to <ExternalLink href={...}>. ExternalLink (web/src/components/ ExternalLink.tsx) is the canonical chokepoint Bundle-8 shipped exactly for this case — it always emits `rel="noopener noreferrer"` and is allowlisted by the L-015 guard. Trade-off: lost the rel="me" identity- claim hint LinkedIn uses (not load-bearing — LinkedIn's verification flow doesn't depend on it); gained the CI gate. Documented in the edit-site comment. ═════════════════ ISSUE 2: CODEQL js/unused-local-variable #35 ═════════════ CodeQL flagged web/src/pages/DashboardPage.tsx:33 — `formatStatus` is defined but never used. Root cause: Phase 4 (commit9ce2d8ca) extracted the four chart panels into pages/dashboard/charts.tsx, which also moved formatStatus + its callers. The local definition in DashboardPage stayed behind as dead code. CodeQL's first detection at868f1c25is just when the alert was raised — the orphan dates from9ce2d8ca. Fix: delete the local formatStatus line, leaving a comment that points to its new home (pages/dashboard/charts.tsx). ══════════════════════════════ VERIFICATION ════════════════════════════════ • npx tsc --noEmit — exits 0 • All 33 CI guards pass locally (bash scripts/ci-guards/*.sh loop — bundle-8-L-015 now green; no-unbound-label still at baseline 132) • Layout 7/7 + DashboardPage 4/4 = 11/11 green • npx vite build — ✓ in 3.30s • grep target="_blank" web/src/components/Layout.tsx → only matches the explanatory comment, not actual JSX • grep formatStatus web/src/pages/DashboardPage.tsx → only matches the explanatory comment, not actual code Next CI run on master should land green.
This commit is contained in:
@@ -51,6 +51,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { useAuth } from './AuthProvider';
|
||||
import { ExternalLink } from './ExternalLink';
|
||||
import logo from '../assets/certctl-logo.png';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -289,18 +290,23 @@ export default function Layout() {
|
||||
the LinkedIn link (the same href + rel="me noopener" pattern
|
||||
the landing page uses). Single-maintainer OSS standard
|
||||
(Cal.com, Plausible, Beekeeper Studio do the same). */}
|
||||
{/* Maintainer attribution row. The Bundle-8 L-015 CI guard line-greps
|
||||
for `target="_blank"` without `rel="noopener noreferrer"` on the
|
||||
SAME LINE — splitting target + rel across lines (as the prior
|
||||
bare <a> did) tripped the guard. ExternalLink is the canonical
|
||||
chokepoint that the guard allowlists. We lose the rel="me" hint
|
||||
(LinkedIn's identity-claim signal, not load-bearing), but gain
|
||||
the CI gate. */}
|
||||
<div className="px-5 pt-3 pb-1 border-t border-white/10">
|
||||
<span className="text-2xs text-sidebar-text/70 font-mono">
|
||||
Built and maintained by{' '}
|
||||
<a
|
||||
<ExternalLink
|
||||
href="https://www.linkedin.com/in/shankar-k-a1b6853ba"
|
||||
target="_blank"
|
||||
rel="me noopener noreferrer"
|
||||
className="text-sidebar-text/90 hover:text-white transition-colors underline-offset-2 hover:underline"
|
||||
title="Shankar on LinkedIn — opens in a new tab"
|
||||
>
|
||||
Shankar
|
||||
</a>
|
||||
</ExternalLink>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -29,8 +29,9 @@ import {
|
||||
// every dashboard load after the operator dismisses it once.
|
||||
const OnboardingWizard = lazy(() => import('./OnboardingWizard'));
|
||||
|
||||
// Convert PascalCase status like "RenewalInProgress" to "Renewal In Progress"
|
||||
const formatStatus = (s: string) => s.replace(/([a-z])([A-Z])/g, '$1 $2');
|
||||
// formatStatus moved to pages/dashboard/charts.tsx in Phase 4 alongside
|
||||
// the memoized chart panels that use it; deleted from here in Hotfix #8
|
||||
// to close CodeQL js/unused-local-variable alert #35.
|
||||
|
||||
const STATUS_COLORS: Record<string, string> = {
|
||||
Active: '#10b981',
|
||||
|
||||
Reference in New Issue
Block a user