From e2912ddba22fc4c80ec82cf66c5e3d46e4f8443a Mon Sep 17 00:00:00 2001
From: emmanuelknafo <48259636+emmanuelknafo@users.noreply.github.com>
Date: Thu, 12 Mar 2026 09:05:25 -0400
Subject: [PATCH 1/2] docs: add SARIF specification research and gap analysis
for GitHub Code Scanning AB#2024
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- research document with full gap analysis and code examples
- GitHub SARIF spec analysis and IBM helpUrl investigation
- SARIF generator analysis identifying missing fields
๐ - Generated by Copilot
---
.../sarif-github-code-scanning-research.md | 411 +++++++++++++++++
.../2026-03-12/github-sarif-spec-research.md | 423 +++++++++++++++++
.../2026-03-12/sarif-generator-analysis.md | 436 ++++++++++++++++++
3 files changed, 1270 insertions(+)
create mode 100644 .copilot-tracking/research/2026-03-12/sarif-github-code-scanning-research.md
create mode 100644 .copilot-tracking/research/subagents/2026-03-12/github-sarif-spec-research.md
create mode 100644 .copilot-tracking/research/subagents/2026-03-12/sarif-generator-analysis.md
diff --git a/.copilot-tracking/research/2026-03-12/sarif-github-code-scanning-research.md b/.copilot-tracking/research/2026-03-12/sarif-github-code-scanning-research.md
new file mode 100644
index 0000000..d56f626
--- /dev/null
+++ b/.copilot-tracking/research/2026-03-12/sarif-github-code-scanning-research.md
@@ -0,0 +1,411 @@
+
+# Task Research: Improve SARIF Output for GitHub Code Scanning
+
+Enhance the SARIF output produced by the accessibility-scanner so that results displayed in GitHub Security > Code Scanning are complete, rich, and useful for developers โ matching the quality of commercial SAST tools like CodeQL.
+
+## Task Implementation Requests
+
+* Add `fullDescription`, `help` (with `text` and `markdown`), and `defaultConfiguration` to SARIF rule descriptors so GitHub shows "Rule help" content inline.
+* Fix broken IBM Equal Access `helpUri` URLs โ two sub-bugs: wrong URL pattern in normalizer, and potential underscore-escaping.
+* Enrich SARIF result `message.text` with structured content including violation details, affected snippet, selector, failure summary, and WCAG criteria.
+* Ensure SARIF `properties` carry proper tags (`precision`, `problem.severity`) so GitHub can categorize/filter/order results.
+* Add `tool.driver.informationUri`, `tool.driver.semanticVersion`, and `automationDetails.id` for proper tool identification.
+
+## Scope and Success Criteria
+
+* Scope: SARIF generator (`src/lib/report/sarif-generator.ts`), result normalizer (`src/lib/scanner/result-normalizer.ts`), and supporting types. Does not cover the web UI, PDF, or HTML report.
+* Assumptions:
+ * GitHub Code Scanning uses a **specific subset** of SARIF v2.1.0 โ not all spec fields are rendered.
+ * `helpUri` is **NOT supported by GitHub** โ help URLs must be embedded in `help.markdown`.
+ * Existing `AxeViolation` and `AxeNode` fields contain all data needed for enrichment.
+ * IBM raw `help` field contains a valid archive URL that should be used directly.
+* Success Criteria:
+ * GitHub displays inline "Rule help" with description, WCAG mapping, remediation guidance, and learn more links for every accessibility alert.
+ * IBM rule links resolve correctly (use archive URL pattern, not the `/tools/help/` pattern).
+ * Result messages include violation description, affected element count, and scanned URL.
+ * Tags/properties enable filtering by WCAG principle and severity ordering.
+
+## Outline
+
+1. Current SARIF output analysis โ complete field inventory and gaps
+2. GitHub SARIF ingestion โ exact fields that enable rich display
+3. IBM Equal Access URL issue โ root cause (two bugs) and fix
+4. Selected approach โ enriched SARIF with all GitHub-supported fields
+5. Implementation plan with code examples
+6. Considered alternatives
+
+## Potential Next Research
+
+* Validate with a test SARIF upload โ confirm `help.markdown` renders as expected.
+* Research whether IBM `/rules/tools/help/{ruleId}` endpoint redirects or is deprecated.
+* Investigate whether `result.message.markdown` is silently supported by GitHub (not documented).
+* Consider adding WCAG success criterion text directly to `help.markdown` using a static mapping table.
+
+## Research Executed
+
+### File Analysis
+
+* `src/lib/report/sarif-generator.ts` (142 lines) โ The entire SARIF generator. Produces `SarifRule` with only 5 fields (`id`, `name`, `shortDescription`, `helpUri`, `properties.tags`). Missing: `fullDescription`, `help`, `defaultConfiguration`, precision/severity properties.
+* `src/lib/scanner/result-normalizer.ts` (lines 60โ86) โ IBM normalizer. Line 74 maps `r.help` (actually a URL) to the text `help` field. Line 75 constructs a different `helpUrl` using `/rules/tools/help/` pattern instead of the raw IBM archive URL.
+* `src/lib/types/scan.ts` (lines 32โ65) โ `AxeViolation` has fields `description`, `help`, `helpUrl`, `nodes`, `principle`, `engine`. `AxeNode` has `html`, `target`, `impact`, `failureSummary`. `failureSummary`, `principle`, and `engine` never reach SARIF.
+* `src/components/ViolationList.tsx` โ HTML report renders all rich data including impact badges, description, snippet, selector, failure summary, element count, and learn more links. This is the quality target for SARIF.
+* `results/https_/example.com.json` (lines 1โ40) โ Raw IBM results. The `help` field contains full archive URLs like `https://able.ibm.com/rules/archives/2026.03.04/doc/en-US/style_color_misuse.html#...` โ these work correctly.
+
+### Code Search Results
+
+* `helpUri` appears only in `sarif-generator.ts` โ set from `violation.helpUrl`.
+* `able.ibm.com` appears in `result-normalizer.ts:75` (constructed URL) and raw result files (archive URL).
+* `fullDescription`, `help.text`, `help.markdown` never appear in the codebase โ confirming they are missing.
+
+### External Research
+
+* GitHub SARIF Support: `https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning`
+ * `fullDescription.text` โ **Required** by GitHub.
+ * `help.text` โ **Required** by GitHub. This is the "Rule help" panel.
+ * `help.markdown` โ **Recommended**. When present, **displayed instead of** `help.text`.
+ * `helpUri` โ **NOT listed** in GitHub's supported properties. GitHub does not render it.
+ * `properties.precision` โ **Recommended**. Affects result ordering.
+ * `properties.problem.severity` โ **Recommended**. Affects result ordering.
+* SARIF v2.1.0 OASIS Spec: `https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html`
+ * ยง3.49.13: `help` is a `multiformatMessageString` with `text` (required) and `markdown` (optional).
+ * ยง3.49.12: `helpUri` is valid SARIF but not guaranteed to be displayed by any viewer.
+
+### Project Conventions
+
+* Standards referenced: SARIF v2.1.0 OASIS spec, GitHub SARIF support docs, CodeQL SARIF patterns
+* Instructions followed: `ado-workflow.instructions.md`, `a11y-remediation.instructions.md`, `wcag22-rules.instructions.md`
+
+## Key Discoveries
+
+### Discovery 1: Root Cause of "No Rule Help Available"
+
+GitHub Code Scanning requires `help.text` and recommends `help.markdown` on every `reportingDescriptor` (rule). The current generator produces neither. This is why **every** accessibility alert shows "no rule help available for this alert."
+
+**Evidence**: GitHub docs explicitly state `help.text` is Required. Current `SarifRule` interface at [sarif-generator.ts](src/lib/report/sarif-generator.ts#L20-L26) only has `id`, `name`, `shortDescription`, `helpUri`, `properties`.
+
+### Discovery 2: `helpUri` Is Ignored by GitHub
+
+GitHub's supported properties documentation does **not list `helpUri`** at all. The current tool relies on `helpUri` for "Learn more" links, but GitHub never renders them. The fix is to embed help URLs as markdown links inside `help.markdown`.
+
+**Evidence**: GitHub SARIF support docs โ `helpUri` absent from the `reportingDescriptor` supported properties table.
+
+### Discovery 3: IBM Equal Access URL โ Two Bugs
+
+**Bug A โ Wrong URL pattern**: `normalizeIbmResults()` at [result-normalizer.ts](src/lib/scanner/result-normalizer.ts#L75) constructs `https://able.ibm.com/rules/tools/help/${r.ruleId}` โ a generic endpoint that may not exist. The raw IBM data contains a working archive URL in the `help` field: `https://able.ibm.com/rules/archives/2026.03.04/doc/en-US/{ruleId}.html#...`.
+
+**Bug B โ Underscore markdown escaping**: Somewhere in the pipeline, underscores in rule IDs (e.g., `label_name_visible`) are backslash-escaped to `label\_name\_visible`, producing 404s. This likely happens when GitHub renders `helpUri` or SARIF content through a markdown processor.
+
+**Fix**: Extract the base URL (before `#` fragment) from the raw IBM `help` field and use it as the canonical help URL. Embed it in `help.markdown` instead of relying on `helpUri`.
+
+### Discovery 4: IBM `help` Field Is a URL, Not Text
+
+The normalizer at [result-normalizer.ts](src/lib/scanner/result-normalizer.ts#L74) does `help: r.help ?? r.message`. But `r.help` in the raw IBM data is a **URL** (e.g., `https://able.ibm.com/rules/archives/...`), not a text description. This means the `help` field of the normalized violation contains a URL string instead of human-readable guidance for IBM rules.
+
+**Evidence**: [example.com.json](results/https_/example.com.json) line 29 โ the `help` field is a full URL with encoded JSON fragment.
+
+### Discovery 5: Rich Data Available But Lost
+
+The HTML report (ViolationList component) displays: `failureSummary`, CSS selectors (`target`), element count, principle grouping, and engine source. None of these flow into SARIF output. The SARIF `message.text` is a simple concatenation: `"{help} ({url} โ {target})"`.
+
+### Discovery 6: `fullDescription.text` Is Missing
+
+GitHub marks `fullDescription.text` as **Required**. The current generator only sets `shortDescription.text` from `violation.description`. `fullDescription` is not set at all.
+
+### Discovery 7: GitHub Upload Limits
+
+| Limit | Value |
+|---|---|
+| File size (gzip) | 10 MB |
+| Runs per file | 20 |
+| Results per run | 25,000 (top 5,000 shown) |
+| Rules per run | 25,000 |
+| Tags per rule | 20 (10 shown) |
+
+### Implementation Patterns
+
+#### Current SarifRule Interface (5 fields)
+
+```typescript
+// sarif-generator.ts lines 20-26
+interface SarifRule {
+ id: string;
+ name: string;
+ shortDescription: { text: string };
+ helpUri: string;
+ properties: { tags: string[] };
+}
+```
+
+#### Target SarifRule Interface (all GitHub-supported fields)
+
+```typescript
+interface SarifRule {
+ id: string;
+ name: string;
+ shortDescription: { text: string };
+ fullDescription: { text: string };
+ helpUri: string; // keep for SARIF compliance, but not rendered by GitHub
+ help: {
+ text: string; // required by GitHub โ plain text rule documentation
+ markdown: string; // recommended โ rich markdown, displayed instead of text
+ };
+ defaultConfiguration: {
+ level: 'error' | 'warning' | 'note';
+ };
+ properties: {
+ tags: string[];
+ precision: 'very-high' | 'high' | 'medium' | 'low';
+ 'problem.severity': 'error' | 'warning' | 'recommendation';
+ };
+}
+```
+
+### Complete Examples
+
+#### Ideal SARIF Rule for an Accessibility Violation
+
+```json
+{
+ "id": "color-contrast",
+ "name": "color-contrast",
+ "shortDescription": {
+ "text": "Ensure the contrast between foreground and background colors meets WCAG 2 AA thresholds"
+ },
+ "fullDescription": {
+ "text": "Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds. Low contrast text is difficult or impossible for many users to read."
+ },
+ "help": {
+ "text": "Ensure sufficient color contrast\n\nElements must meet minimum color contrast ratio thresholds.\n\nFix: Increase the contrast ratio between foreground and background colors. Use at least 4.5:1 for normal text and 3:1 for large text.\n\nWCAG: 1.4.3 Contrast (Minimum) (Level AA)\n\nLearn more: https://dequeuniversity.com/rules/axe/4.10/color-contrast",
+ "markdown": "# Ensure sufficient color contrast\n\nElements must meet minimum color contrast ratio thresholds.\n\n## Why This Matters\n\nLow contrast text is difficult or impossible to read for many users, including those with low vision, color blindness, or age-related vision changes.\n\n## How to Fix\n\n- Increase the contrast ratio between foreground and background colors\n- Use a contrast ratio of at least **4.5:1** for normal text\n- Use a contrast ratio of at least **3:1** for large text (18pt or 14pt bold)\n\n## WCAG Criteria\n\n- [1.4.3 Contrast (Minimum) (Level AA)](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)\n\n## Learn More\n\n- [Deque University: color-contrast](https://dequeuniversity.com/rules/axe/4.10/color-contrast)\n"
+ },
+ "helpUri": "https://dequeuniversity.com/rules/axe/4.10/color-contrast",
+ "defaultConfiguration": {
+ "level": "error"
+ },
+ "properties": {
+ "tags": ["accessibility", "WCAG2AA", "wcag143"],
+ "precision": "very-high",
+ "problem.severity": "error"
+ }
+}
+```
+
+#### Ideal SARIF Result
+
+```json
+{
+ "ruleId": "color-contrast",
+ "ruleIndex": 0,
+ "level": "error",
+ "message": {
+ "text": "Ensure sufficient color contrast: Elements must meet minimum color contrast ratio thresholds. Scanned URL: https://example.com โ Selector: button.btn-secondary โ 3 elements affected"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": { "uri": "example.com/index" },
+ "region": {
+ "startLine": 1,
+ "startColumn": 1,
+ "snippet": {
+ "text": ""
+ }
+ }
+ }
+ }
+ ],
+ "partialFingerprints": {
+ "primaryLocationLineHash": "a1b2c3d4"
+ }
+}
+```
+
+### Configuration Examples
+
+#### IBM helpUrl Fix in result-normalizer.ts
+
+```typescript
+// Extract base URL from IBM help field (strip fragment with encoded JSON context)
+function extractIbmHelpUrl(rawHelp: string | undefined): string {
+ if (!rawHelp) return '';
+ try {
+ const url = new URL(rawHelp);
+ return `${url.origin}${url.pathname}`; // strip #fragment
+ } catch {
+ return rawHelp;
+ }
+}
+
+// In normalizeIbmResults():
+helpUrl: extractIbmHelpUrl(r.help) || `https://able.ibm.com/rules/archives/latest/doc/en-US/${r.ruleId}.html`,
+help: r.message, // Use the message text, not the URL, as the help text
+```
+
+#### help.markdown Build Function
+
+```typescript
+function buildHelpMarkdown(violation: AxeViolation): string {
+ const wcagTags = violation.tags.filter(t => /^wcag\d/.test(t));
+ const lines: string[] = [
+ `# ${violation.help}`,
+ '',
+ violation.description,
+ '',
+ `**Impact:** ${violation.impact}`,
+ ];
+
+ if (violation.principle) {
+ lines.push(`**Principle:** ${violation.principle}`);
+ }
+
+ if (wcagTags.length > 0) {
+ lines.push('', '## WCAG Criteria', '');
+ for (const tag of wcagTags) {
+ lines.push(`- \`${tag}\``);
+ }
+ }
+
+ lines.push('', '## Learn More', '');
+ lines.push(`- [Rule documentation](${violation.helpUrl})`);
+
+ return lines.join('\n');
+}
+```
+
+## Technical Scenarios
+
+### Scenario: Enriched SARIF Rule Descriptors
+
+The core problem is that GitHub cannot display rule help because the `help` property is missing from every `reportingDescriptor` in the SARIF output.
+
+**Requirements:**
+
+* Every rule must have `fullDescription.text`, `help.text`, and `help.markdown`.
+* `help.markdown` should include: rule title, description, impact, WCAG criteria, principle, and a learn more link.
+* `defaultConfiguration.level` must map from `violation.impact`.
+* `properties` must include `precision` and `problem.severity`.
+
+**Preferred Approach:**
+
+Build a `buildHelpMarkdown()` function that generates structured markdown from `AxeViolation` data. Update the `SarifRule` interface and the `buildRun()` function to populate all GitHub-required fields. Keep `helpUri` for SARIF spec compliance but do not rely on it for display.
+
+```text
+src/lib/report/sarif-generator.ts (modify โ add fields to interface and buildRun)
+src/lib/scanner/result-normalizer.ts (modify โ fix IBM helpUrl and help text)
+```
+
+```mermaid
+flowchart TD
+ A[AxeViolation] -->|id, description, help, helpUrl, impact, tags, principle| B[buildRun]
+ B --> C[SarifRule]
+ C -->|fullDescription.text| D[violation.description]
+ C -->|help.text| E[buildHelpText]
+ C -->|help.markdown| F[buildHelpMarkdown]
+ C -->|defaultConfiguration.level| G[mapImpactToLevel]
+ C -->|properties.precision| H[mapEngineToPrecision]
+ C -->|properties.problem.severity| I[mapImpactToSeverity]
+ B --> J[SarifResult]
+ J -->|message.text| K[enriched message with URL, selector, element count]
+```
+
+**Implementation Details:**
+
+1. Update `SarifRule` interface to include all GitHub-supported fields.
+2. Add `buildHelpMarkdown(violation)` function.
+3. Add `buildHelpText(violation)` function (plain text fallback).
+4. Update `buildRun()` to populate new fields on each rule.
+5. Enrich `SarifResult.message.text` with description and element count.
+6. Add `tool.driver.informationUri` and `tool.driver.semanticVersion`.
+
+#### Considered Alternatives
+
+**Alternative A: Minimal fix โ only add `help.text`**
+
+* Pros: Smallest change, addresses the "no rule help" issue.
+* Cons: Loses the opportunity for rich markdown display. No structured help.
+* Rejected because: `help.markdown` is the key differentiator for useful alerts. Minimal effort to add both.
+
+**Alternative B: Generate help content from a static WCAG mapping table**
+
+* Pros: Could provide detailed WCAG success criterion text and specific remediation guidance.
+* Cons: Requires maintaining a mapping table. Current data already contains `description` and `help` fields with useful content.
+* Rejected because: The violation data already contains sufficient content. A static table may become stale. Can be added later as an enhancement.
+
+**Alternative C: Use `result.message.markdown` for rich results**
+
+* Pros: Could render rich markdown in the result detail view.
+* Cons: GitHub docs do **not** list `message.markdown` as a supported property. Relies on undocumented behavior.
+* Rejected because: Unreliable. Use `message.text` with information-dense first sentence instead.
+
+### Scenario: Fix IBM Equal Access URLs
+
+The root cause is two bugs: wrong URL pattern in the normalizer, and the IBM `help` field being treated as text when it is actually a URL.
+
+**Requirements:**
+
+* IBM rule `helpUrl` must use the working archive URL pattern from the raw IBM data.
+* The `help` text on the normalized violation must be human-readable (the message), not a URL.
+* URLs in `help.markdown` must not have underscores escaped.
+
+**Preferred Approach:**
+
+Extract the base URL (before `#` fragment) from the raw IBM `help` field: `r.help`. Use it as `helpUrl`. Use `r.message` as the human-readable `help` text. If `r.help` is not a valid URL, fall back to constructing the archive URL from `r.ruleId`.
+
+```text
+src/lib/scanner/result-normalizer.ts (modify lines 74-75)
+```
+
+**Implementation Details:**
+
+```typescript
+// Add helper function
+function extractIbmHelpUrl(rawHelp: string | undefined, ruleId: string): string {
+ if (rawHelp) {
+ try {
+ const url = new URL(rawHelp);
+ return `${url.origin}${url.pathname}`; // strip #fragment with encoded JSON
+ } catch {
+ // not a URL, fall through
+ }
+ }
+ return `https://able.ibm.com/rules/archives/latest/doc/en-US/${ruleId}.html`;
+}
+
+// In normalizeIbmResults():
+help: r.message, // human-readable text, not the URL
+helpUrl: extractIbmHelpUrl(r.help, r.ruleId),
+```
+
+#### Considered Alternatives
+
+**Alternative: Keep the `/rules/tools/help/{ruleId}` pattern**
+
+* Rejected because: This URL pattern produces 404s. The raw IBM data provides working archive URLs.
+
+**Alternative: Hardcode a specific archive version like `2026.03.04`**
+
+* Rejected because: The version changes over time. Extracting from the raw data is forward-compatible.
+
+## Summary
+
+| Area | Current State | Target State | Priority |
+|---|---|---|---|
+| `rules[].help.text` | Missing | Build from `violation.help` + `description` | **P0** |
+| `rules[].help.markdown` | Missing | Rich markdown with title, impact, WCAG, learn more | **P0** |
+| `rules[].fullDescription.text` | Missing | Set to `violation.description` | **P0** |
+| IBM helpUrl | Wrong URL pattern | Extract from raw IBM `help` field | **P0** |
+| IBM help text | Contains URL string | Use `r.message` instead | **P0** |
+| `rules[].defaultConfiguration.level` | Missing | Map from `violation.impact` | **P1** |
+| `rules[].properties.precision` | Missing | Map from engine (`very-high` for axe, `high` for IBM) | **P1** |
+| `rules[].properties.problem.severity` | Missing | Map from impact | **P1** |
+| `result.message.text` | Basic concatenation | Enriched: description + URL + element count | **P1** |
+| `tool.driver.informationUri` | Missing | Link to GitHub repo | **P2** |
+| `tool.driver.semanticVersion` | Missing | Same as version | **P2** |
+| `helpUri` | Present but ignored | Keep for spec compliance | **P2** |
+
+### Files to Modify
+
+1. **`src/lib/report/sarif-generator.ts`** โ Update `SarifRule` interface, add `buildHelpMarkdown()` and `buildHelpText()`, update `buildRun()` to populate all new fields, enrich `message.text`.
+2. **`src/lib/scanner/result-normalizer.ts`** โ Fix IBM `helpUrl` construction (extract from raw `help` field), fix IBM `help` text (use `r.message`).
+3. **`src/lib/report/__tests__/sarif-generator.test.ts`** โ Update tests for new fields.
+4. **`src/lib/scanner/__tests__/result-normalizer.test.ts`** โ Update IBM helpUrl and help text tests.
diff --git a/.copilot-tracking/research/subagents/2026-03-12/github-sarif-spec-research.md b/.copilot-tracking/research/subagents/2026-03-12/github-sarif-spec-research.md
new file mode 100644
index 0000000..9ee30b9
--- /dev/null
+++ b/.copilot-tracking/research/subagents/2026-03-12/github-sarif-spec-research.md
@@ -0,0 +1,423 @@
+# GitHub SARIF Specification Research for Code Scanning Display
+
+**Status:** Complete
+**Date:** 2026-03-12
+**Topic:** SARIF v2.1.0 fields that produce rich display in GitHub Security tab
+
+---
+
+## 1. Executive Summary
+
+GitHub Code Scanning uses a **specific subset** of SARIF v2.1.0 properties. The **critical missing piece** causing "no rule help available" in the current accessibility scanner is the absence of the `help` property (`help.text` and `help.markdown`) on `reportingDescriptor` (rule) objects. Additionally, the current generator is missing `fullDescription` on rules, which GitHub marks as **Required**.
+
+### Key Fix: Add `help.text` and `help.markdown` to every rule
+
+---
+
+## 2. Complete Field Mapping: What GitHub Uses for Display
+
+### 2.1 reportingDescriptor (rules[]) โ The Most Important Object
+
+This is where rule metadata lives. GitHub's documentation explicitly lists these properties:
+
+| Property | Required? | GitHub Display Usage |
+|---|---|---|
+| `id` | **Required** | Unique rule identifier. Used in URLs, filtering, and cross-referencing. |
+| `name` | Optional | Displayed to allow filtering by rule. Limited to **255 characters**. |
+| `shortDescription.text` | **Required** | Displayed next to associated results. Limited to **1024 characters**. |
+| `fullDescription.text` | **Required** | Displayed next to associated results. Limited to **1024 characters**. |
+| `defaultConfiguration.level` | Optional | Default severity: `note`, `warning`, `error`. Defaults to `warning`. |
+| `help.text` | **Required** | Help documentation shown next to results. **This is the rule help panel content.** |
+| `help.markdown` | Optional (Recommended) | **When present, displayed INSTEAD of `help.text`**. This is the rich expandable help content shown in alert detail. |
+| `helpUri` | Not listed as supported | **NOT in GitHub's supported properties table.** See section 2.6 below. |
+| `properties.tags[]` | Optional | Array of strings for filtering results on GitHub (e.g., `security`, `accessibility`). |
+| `properties.precision` | Optional (Recommended) | `very-high`, `high`, `medium`, `low`. Results ordered by precision. |
+| `properties.problem.severity` | Optional (Recommended) | For non-security: `error`, `warning`, `recommendation`. |
+| `properties.security-severity` | Optional (Recommended for security) | Numeric `0.0โ10.0`. Triggers security severity mapping: >9.0=critical, 7.0โ8.9=high, 4.0โ6.9=medium, 0.1โ3.9=low. |
+
+### 2.2 result object โ Per-Alert Data
+
+| Property | Required? | GitHub Display Usage |
+|---|---|---|
+| `ruleId` | Optional | Rule identifier. Used for filtering by rule. |
+| `ruleIndex` | Optional | Index into `rules[]` array. |
+| `rule` | Optional | Reference to the reporting descriptor. |
+| `level` | Optional | Overrides `defaultConfiguration.level`. Values: `note`, `warning`, `error`. |
+| `message.text` | **Required** | **Alert title/description.** First sentence shown when space is limited. |
+| `locations[]` | **Required** | Physical locations. At least one required. Only first used for file annotation. Max 10. |
+| `partialFingerprints` | **Required** | Fingerprint for deduplication. Only `primaryLocationLineHash` is used. |
+| `codeFlows[].threadFlows[].locations[]` | Optional | If present, GitHub expands code flow visualization. |
+| `relatedLocations[]` | Optional | Linked when embedded in result message via `[text](id)` syntax. |
+
+### 2.3 physicalLocation object
+
+| Property | Required? |
+|---|---|
+| `artifactLocation.uri` | **Required** โ relative path from repo root recommended. |
+| `region.startLine` | **Required** |
+| `region.startColumn` | **Required** |
+| `region.endLine` | **Required** |
+| `region.endColumn` | **Required** |
+
+### 2.4 toolComponent object
+
+| Property | Required? |
+|---|---|
+| `name` | **Required** |
+| `version` | Optional (not used if `semanticVersion` present) |
+| `semanticVersion` | Optional (preferred over `version`) |
+| `rules[]` | **Required** |
+
+### 2.5 sarifLog object
+
+| Property | Required? |
+|---|---|
+| `$schema` | **Required** โ e.g., `https://json.schemastore.org/sarif-2.1.0.json` |
+| `version` | **Required** โ must be `"2.1.0"` |
+| `runs[]` | **Required** |
+
+### 2.6 helpUri โ The Missing "Learn More" Link
+
+**Critical finding:** `helpUri` is **NOT listed in GitHub's supported properties table** for `reportingDescriptor`. GitHub's documentation does not mention `helpUri` at all in its supported properties section.
+
+This means:
+- GitHub does **not** render `helpUri` as a clickable "Learn more" link in the alert detail.
+- If a "Learn more" link is desired, include it as a markdown link **within `help.markdown`**.
+- The current broken links in alerts are likely because `helpUri` is the only reference and GitHub ignores it.
+
+**Workaround:** Embed the help URL directly in `help.markdown`:
+```markdown
+[Learn more about this rule](https://dequeuniversity.com/rules/axe/4.10/color-contrast)
+```
+
+---
+
+## 3. The `help` Property โ The Key Missing Piece
+
+### 3.1 What It Does
+
+Per SARIF v2.1.0 ยง3.49.13: The `help` property is a `multiformatMessageString` object containing:
+- `text` (required on the object): Plain text documentation for the rule.
+- `markdown` (optional): GitHub Flavored Markdown documentation.
+
+Per GitHub's documentation:
+> **`help.text`** โ Required. Documentation for the rule using text format. Code scanning displays this help documentation next to the associated results.
+>
+> **`help.markdown`** โ Optional (Recommended). Documentation for the rule using Markdown format. Code scanning displays this help documentation next to the associated results. **When `help.markdown` is available, it is displayed instead of `help.text`.**
+
+### 3.2 How CodeQL Structures `help.markdown`
+
+CodeQL produces rich alerts with structured markdown help. The pattern is:
+
+```json
+{
+ "id": "js/xss",
+ "name": "CrossSiteScripting",
+ "shortDescription": {
+ "text": "Cross-site scripting vulnerability"
+ },
+ "fullDescription": {
+ "text": "Writing user input directly to a web page allows for a cross-site scripting vulnerability."
+ },
+ "help": {
+ "text": "# Cross-site scripting\n\nWriting user input directly to a web page...",
+ "markdown": "# Cross-site scripting\n\nWriting user input directly to a web page allows for a cross-site scripting vulnerability.\n\n## Recommendation\n\nSanitize all user input before...\n\n## Example\n\n```javascript\n// BAD\nresponse.write(req.query.name);\n```\n\n## References\n\n- [OWASP XSS Prevention](https://example.com)\n"
+ },
+ "defaultConfiguration": {
+ "level": "error"
+ },
+ "properties": {
+ "tags": ["security", "external/cwe/cwe-079"],
+ "precision": "high",
+ "security-severity": "6.1"
+ }
+}
+```
+
+### 3.3 Recommended `help.markdown` Structure for Accessibility Rules
+
+```markdown
+# Rule Title (e.g., "Ensure sufficient color contrast")
+
+Brief description of what the rule checks.
+
+## Why This Matters
+
+Explanation of accessibility impact and who is affected.
+
+## How to Fix
+
+Step-by-step remediation guidance.
+
+## WCAG Criteria
+
+- [WCAG 2.2 Success Criterion X.Y.Z](https://www.w3.org/WAI/WCAG22/Understanding/...)
+
+## Learn More
+
+- [Deque University: rule-name](https://dequeuniversity.com/rules/axe/4.10/rule-name)
+- [WCAG Understanding Document](https://www.w3.org/WAI/WCAG22/Understanding/...)
+```
+
+---
+
+## 4. `result.message` โ Alert Title Display
+
+### 4.1 message.text
+
+**Required.** GitHub displays `message.text` as the alert title. Per GitHub docs:
+> Only the first sentence of the message will be displayed when visible space is limited.
+
+### 4.2 message.markdown
+
+The SARIF spec (ยง3.11.9) supports `markdown` on message objects. However, GitHub's supported properties table for `result` only lists `message.text`. GitHub does **not** list `message.markdown` as a supported property.
+
+**Recommendation:** Use `message.text` with a clear, information-dense first sentence. Do not rely on `message.markdown` for result messages.
+
+---
+
+## 5. Severity Mapping
+
+### 5.1 defaultConfiguration.level
+
+Maps directly to GitHub severity badges:
+- `"error"` โ Error (red)
+- `"warning"` โ Warning (yellow)
+- `"note"` โ Note (blue)
+
+Defaults to `"warning"` if absent.
+
+### 5.2 properties.security-severity
+
+For rules tagged with `security` in `properties.tags`, this numeric score (0.0โ10.0) maps to:
+- **>9.0** โ Critical
+- **7.0โ8.9** โ High
+- **4.0โ6.9** โ Medium
+- **0.1โ3.9** โ Low
+
+### 5.3 properties.problem.severity
+
+For non-security rules: `error`, `warning`, `recommendation`. Used with `precision` to order results.
+
+### 5.4 Recommended Mapping for Accessibility
+
+| axe-core Impact | `defaultConfiguration.level` | `properties.problem.severity` |
+|---|---|---|
+| critical | `error` | `error` |
+| serious | `error` | `error` |
+| moderate | `warning` | `warning` |
+| minor | `note` | `recommendation` |
+
+---
+
+## 6. Tags and Filtering
+
+`properties.tags[]` on rules allows GitHub filtering. Max **20 tags** per rule (only 10 displayed).
+
+Recommended tags for accessibility:
+```json
+{
+ "tags": [
+ "accessibility",
+ "WCAG2.2",
+ "WCAG2.1",
+ "level-A", // or "level-AA"
+ "cat.color", // axe-core category
+ "best-practice" // for best-practice rules
+ ]
+}
+```
+
+---
+
+## 7. Fingerprinting and Deduplication
+
+### 7.1 partialFingerprints
+
+**Required** by GitHub. Only `primaryLocationLineHash` is used.
+
+GitHub computes fingerprints from `partialFingerprints` if provided. The `upload-sarif` action can auto-compute if missing, but the API endpoint cannot.
+
+### 7.2 Recommended approach
+
+Include a hash based on: `ruleId + target selector + page URL`.
+
+---
+
+## 8. Upload Limits
+
+| Limit | Value | Notes |
+|---|---|---|
+| File size (gzip) | **10 MB** max | |
+| Runs per file | **20** | |
+| Results per run | **25,000** | Only top 5,000 shown (by severity) |
+| Rules per run | **25,000** | |
+| Tool extensions per run | **100** | |
+| Thread flow locations per result | **10,000** | Only top 1,000 shown |
+| Locations per result | **1,000** | Only 100 shown |
+| Tags per rule | **20** | Only 10 shown |
+| Total alert limit | **1,000,000** | |
+
+---
+
+## 9. Gaps in Current SARIF Generator
+
+Comparing the existing `sarif-generator.ts` against GitHub requirements:
+
+| Field | Current State | Required State | Priority |
+|---|---|---|---|
+| `rules[].fullDescription` | **MISSING** | Required by GitHub | **P0** |
+| `rules[].help.text` | **MISSING** | Required by GitHub | **P0** |
+| `rules[].help.markdown` | **MISSING** | Recommended โ renders rich help | **P0** |
+| `rules[].helpUri` | Present | **Not used by GitHub** โ remove or keep | P2 |
+| `rules[].defaultConfiguration.level` | **MISSING** | Optional but important for severity | **P1** |
+| `rules[].properties.precision` | **MISSING** | Recommended for ordering | P1 |
+| `rules[].properties.problem.severity` | **MISSING** | Recommended for ordering | P1 |
+| `rules[].properties.security-severity` | **MISSING** | Only for security-tagged rules | P2 |
+| `result.locations[].region.startLine` | **MISSING** (only snippet) | Required by GitHub | **P0** |
+| `result.locations[].region.startColumn` | **MISSING** | Required by GitHub | **P0** |
+| `result.locations[].region.endLine` | **MISSING** | Required by GitHub | **P0** |
+| `result.locations[].region.endColumn` | **MISSING** | Required by GitHub | **P0** |
+| `$schema` | Uses OASIS raw URL | Should use `https://json.schemastore.org/sarif-2.1.0.json` | P2 |
+
+---
+
+## 10. Ideal SARIF Structure โ Complete Example
+
+```json
+{
+ "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
+ "version": "2.1.0",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "accessibility-scanner",
+ "semanticVersion": "1.0.0",
+ "informationUri": "https://github.com/devopsabcs-engineering/accessibility-scan-demo-app",
+ "rules": [
+ {
+ "id": "color-contrast",
+ "name": "color-contrast",
+ "shortDescription": {
+ "text": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds."
+ },
+ "fullDescription": {
+ "text": "Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds. Low contrast text is difficult or impossible for many users to read."
+ },
+ "help": {
+ "text": "Elements must meet minimum color contrast ratio thresholds.\n\nFix any of the following:\n- Increase the contrast ratio between the foreground and background colors.\n- Use larger or bolder text.\n\nWCAG Criteria: 1.4.3 Contrast (Minimum) (Level AA)\n\nLearn more: https://dequeuniversity.com/rules/axe/4.10/color-contrast",
+ "markdown": "# Ensure sufficient color contrast\n\nElements must meet minimum color contrast ratio thresholds.\n\n## Why This Matters\n\nLow contrast text is difficult or impossible to read for many users, including those with low vision, color blindness, or age-related vision changes.\n\n## How to Fix\n\n- Increase the contrast ratio between foreground and background colors\n- Use a contrast ratio of at least **4.5:1** for normal text\n- Use a contrast ratio of at least **3:1** for large text (18pt or 14pt bold)\n\n## WCAG Criteria\n\n- [1.4.3 Contrast (Minimum) (Level AA)](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)\n\n## Learn More\n\n- [Deque University: color-contrast](https://dequeuniversity.com/rules/axe/4.10/color-contrast)\n"
+ },
+ "helpUri": "https://dequeuniversity.com/rules/axe/4.10/color-contrast",
+ "defaultConfiguration": {
+ "level": "error"
+ },
+ "properties": {
+ "tags": [
+ "accessibility",
+ "WCAG2AA",
+ "cat.color",
+ "wcag143"
+ ],
+ "precision": "very-high",
+ "problem.severity": "error"
+ }
+ }
+ ]
+ }
+ },
+ "results": [
+ {
+ "ruleId": "color-contrast",
+ "ruleIndex": 0,
+ "level": "error",
+ "message": {
+ "text": "Element has insufficient color contrast of 2.52 (foreground: #6c757d, background: #ffffff, required ratio: 4.5:1). Found on https://example.com โ button.btn-secondary"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "example.com/index.html"
+ },
+ "region": {
+ "startLine": 1,
+ "startColumn": 1,
+ "endLine": 1,
+ "endColumn": 2,
+ "snippet": {
+ "text": ""
+ }
+ }
+ }
+ }
+ ],
+ "partialFingerprints": {
+ "primaryLocationLineHash": "a1b2c3d4:1"
+ }
+ }
+ ],
+ "columnKind": "utf16CodeUnits"
+ }
+ ]
+}
+```
+
+---
+
+## 11. relatedLocations and Embedded Links
+
+GitHub supports `relatedLocations[]` with embedded links in `message.text`:
+
+```json
+{
+ "message": {
+ "text": "Element has insufficient contrast. See [related element](0)."
+ },
+ "relatedLocations": [
+ {
+ "id": 0,
+ "physicalLocation": { ... },
+ "message": { "text": "Related element" }
+ }
+ ]
+}
+```
+
+The `[text](id)` syntax in message text creates clickable links to related locations.
+
+---
+
+## 12. codeFlows
+
+GitHub will expand `codeFlows` if present. For accessibility scanning, this is generally not applicable since violations are typically single-location findings rather than execution path issues.
+
+---
+
+## 13. How helpUri Is (Not) Processed
+
+**Key Finding:** `helpUri` is defined in SARIF v2.1.0 spec (ยง3.49.12) as a localizable absolute URI for primary documentation. However, GitHub's supported properties documentation for `reportingDescriptor` does **not list `helpUri`** at all.
+
+This means:
+1. GitHub likely **ignores** `helpUri` entirely.
+2. The "broken links" reported in the current tool are likely because `helpUri` is the only reference URL, and GitHub never renders it.
+3. **Solution:** Embed help URLs in `help.markdown` as markdown links.
+
+Note: It is still harmless to include `helpUri` in the SARIF output (it's valid SARIF), but do not rely on it for display.
+
+---
+
+## 14. References
+
+- [GitHub SARIF Support Documentation](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning)
+- [SARIF v2.1.0 Specification (OASIS)](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html)
+- [SARIF JSON Schema](https://json.schemastore.org/sarif-2.1.0.json)
+- [GitHub CodeQL Action Fingerprints](https://github.com/github/codeql-action/blob/main/src/fingerprints.ts)
+- [Microsoft SARIF Validator](https://sarifweb.azurewebsites.net/)
+
+---
+
+## 15. Clarifying Questions
+
+None โ all research questions have been answered through the GitHub documentation and SARIF specification.
diff --git a/.copilot-tracking/research/subagents/2026-03-12/sarif-generator-analysis.md b/.copilot-tracking/research/subagents/2026-03-12/sarif-generator-analysis.md
new file mode 100644
index 0000000..24aa46c
--- /dev/null
+++ b/.copilot-tracking/research/subagents/2026-03-12/sarif-generator-analysis.md
@@ -0,0 +1,436 @@
+# SARIF Generator Gap Analysis for GitHub Code Scanning
+
+## Research Topics
+
+1. Current SARIF fields produced vs. SARIF v2.1.0 spec requirements for GitHub Code Scanning
+2. IBM Equal Access URL pattern mismatch and encoding issues
+3. Data available in AxeViolation/AxeNode that should flow into SARIF
+4. Missing SARIF properties that GitHub Code Scanning needs for rich display
+
+## Status: Complete
+
+---
+
+## 1. Current SARIF Output โ Field Inventory
+
+### Source File
+
+`src/lib/report/sarif-generator.ts` โ 142 lines total.
+
+### Current SarifRule (reportingDescriptor) Fields
+
+| Field | Present | Value Source |
+|---|---|---|
+| `id` | Yes | `violation.id` |
+| `name` | Yes | `violation.id` (same as id โ not a human-readable name) |
+| `shortDescription.text` | Yes | `violation.description` |
+| `helpUri` | Yes | `violation.helpUrl` (passed through from normalizer) |
+| `properties.tags` | Yes | `violation.tags` |
+| `fullDescription.text` | **MISSING** | GitHub marks this **Required** |
+| `help.text` | **MISSING** | GitHub marks this **Required** |
+| `help.markdown` | **MISSING** | GitHub **Recommended** โ when present, displayed instead of `help.text` |
+| `defaultConfiguration.level` | **MISSING** | GitHub **Optional** but used for severity display |
+| `properties.precision` | **MISSING** | GitHub **Recommended** โ affects result ordering |
+| `properties.problem.severity` | **MISSING** | GitHub **Recommended** โ affects result ordering |
+
+### Current SarifResult Fields
+
+| Field | Present | Value Source |
+|---|---|---|
+| `ruleId` | Yes | `violation.id` |
+| `ruleIndex` | Yes | Index into rules array |
+| `level` | Yes | Mapped from `violation.impact` via `mapImpactToLevel()` |
+| `message.text` | Yes | `"{help} ({url} โ {target})"` โ plain text only |
+| `locations[0].physicalLocation.artifactLocation.uri` | Yes | `urlToArtifactPath(url)` โ converts URL to hostname/path |
+| `locations[0].physicalLocation.region.snippet.text` | Yes | `node.html` |
+| `partialFingerprints.primaryLocationLineHash` | Yes | Simple hash of `violation.id:target` |
+| `relatedLocations` | **MISSING** | Could link multiple affected nodes |
+| `codeFlows` | **MISSING** | Not applicable for accessibility |
+| `locations[0].physicalLocation.region.startLine` | **MISSING** | Not available from DOM scanning |
+| `locations[0].physicalLocation.region.startColumn` | **MISSING** | Not available from DOM scanning |
+| `locations[0].message.text` | **MISSING** | Could carry failure summary |
+
+### Current SarifRun Fields
+
+| Field | Present | Value Source |
+|---|---|---|
+| `tool.driver.name` | Yes | `'accessibility-scanner'` |
+| `tool.driver.version` | Yes | Passed as parameter |
+| `tool.driver.rules` | Yes | Built from violations |
+| `results` | Yes | Built from violation nodes |
+| `tool.driver.semanticVersion` | **MISSING** | GitHub prefers this over `version` |
+| `tool.driver.informationUri` | **MISSING** | Link to tool documentation |
+| `automationDetails.id` | **MISSING** | Enables category-based filtering |
+| `columnKind` | **MISSING** | Should be `"utf16CodeUnits"` per spec examples |
+
+### Top-Level SarifLog Fields
+
+| Field | Present | Notes |
+|---|---|---|
+| `$schema` | Yes | Uses OASIS spec URL |
+| `version` | Yes | `'2.1.0'` |
+| `runs` | Yes | One per URL scanned |
+
+---
+
+## 2. IBM Equal Access URL Issue โ Root Cause Analysis
+
+### Two Different URL Patterns
+
+**Pattern A โ IBM raw result `help` field (from actual scan data):**
+
+```text
+https://able.ibm.com/rules/archives/2026.03.04/doc/en-US/style_color_misuse.html#...
+```
+
+This is the *actual* URL embedded in the IBM Equal Access engine results. It points to a versioned archive path and includes rule-specific URL fragment with encoded JSON context. These URLs work correctly.
+
+**Pattern B โ result-normalizer.ts line 75 (hardcoded in normalizer):**
+
+```text
+https://able.ibm.com/rules/tools/help/${r.ruleId}
+```
+
+This is a *different, generic* URL pattern that the normalizer substitutes. It uses `/rules/tools/help/` instead of the versioned archive path.
+
+### The Core Problem
+
+The `normalizeIbmResults()` function at `src/lib/scanner/result-normalizer.ts:75` **discards the IBM raw `help` URL** and replaces it with a constructed URL:
+
+```typescript
+helpUrl: `https://able.ibm.com/rules/tools/help/${r.ruleId}`,
+```
+
+However, the IBM raw result object has a working `help` field containing the full archive URL:
+
+```json
+"help": "https://able.ibm.com/rules/archives/2026.03.04/doc/en-US/style_color_misuse.html#..."
+```
+
+The normalizer maps `r.help ?? r.message` to the `help` text field, treating the IBM `help` property as a text description rather than recognizing it as a URL.
+
+### The Underscore Encoding Issue
+
+The user reports GitHub Code Scanning showing broken links like:
+
+```text
+Cannot GET /rules/archives/2026.03.04/doc/en-US/label\_name\_visible.html
+```
+
+Note the `\_` (backslash-escaped underscores). This happens because:
+
+1. The SARIF `helpUri` contains a URL with underscores in the rule ID (e.g., `label_name_visible`)
+2. Somewhere in the rendering pipeline (likely GitHub's markdown processing of SARIF content), underscores are being treated as markdown emphasis delimiters and backslash-escaped
+3. The escaped underscores (`\_`) are passed through to the HTTP request, resulting in a 404
+
+### Root Cause Summary
+
+There are actually **two bugs**:
+
+1. **Wrong URL pattern**: The normalizer substitutes `/rules/tools/help/{ruleId}` instead of using the IBM-provided archive URL (`/rules/archives/{version}/doc/en-US/{ruleId}.html`)
+2. **Potential markdown escaping**: If the URL passes through any markdown processing step, underscores in rule IDs like `label_name_visible` get backslash-escaped to `label\_name\_visible`
+
+### Fix Approach
+
+- Use the IBM raw `help` URL directly (strip the `#fragment` portion containing encoded JSON context)
+- Or construct URLs using the archive pattern: `https://able.ibm.com/rules/archives/{version}/doc/en-US/{ruleId}.html`
+- Ensure `helpUri` URLs are never processed as markdown
+
+---
+
+## 3. Data Available in AxeViolation/AxeNode Not Flowing to SARIF
+
+### AxeViolation Fields (from `src/lib/types/scan.ts`)
+
+| Field | Type | Used in SARIF | Notes |
+|---|---|---|---|
+| `id` | `string` | Yes โ `ruleId`, `rule.id`, `rule.name` | `name` should be human-readable, not same as `id` |
+| `impact` | `'minor'\|'moderate'\|'serious'\|'critical'` | Yes โ mapped to `level` | Also usable for `defaultConfiguration.level` |
+| `tags` | `string[]` | Yes โ `properties.tags` | Could also drive `properties.precision` |
+| `description` | `string` | Yes โ `shortDescription.text` | Should also populate `fullDescription.text` |
+| `help` | `string` | Partial โ used in `message.text` | **Should populate `help.text`** |
+| `helpUrl` | `string` | Yes โ `helpUri` | Broken for IBM (see section 2) |
+| `nodes` | `AxeNode[]` | Partial | Only first node's html used as snippet |
+| `principle` | `string?` | **No** | Could be added to `properties.tags` or `properties` bag |
+| `engine` | `string?` | **No** | Could be added to `properties` bag |
+
+### AxeNode Fields (from `src/lib/types/scan.ts`)
+
+| Field | Type | Used in SARIF | Notes |
+|---|---|---|---|
+| `html` | `string` | Yes โ `region.snippet.text` | Good |
+| `target` | `string[]` | Partial โ in `message.text` | Could be in `location.message.text` |
+| `impact` | `string` | **No** | Node-level impact ignored |
+| `failureSummary` | `string?` | **No** | **Rich data lost** โ should appear in help or message |
+
+### Data Available in HTML Report But Missing from SARIF
+
+The `ViolationList.tsx` component renders all this data for each violation:
+
+1. **Impact badge** โ severity level (critical/serious/moderate/minor) โ โ in SARIF as `level`
+2. **Help text** (violation.help) โ the concise rule summary โ โ NOT in SARIF `help.text`
+3. **Rule ID** (violation.id) โ โ in SARIF
+4. **Affected elements count** (violation.nodes.length) โ โ NOT in SARIF
+5. **Description** (violation.description) โ โ in `shortDescription` only
+6. **HTML snippet** per node (node.html) โ โ in `region.snippet.text`
+7. **Failure summary** per node (node.failureSummary) โ โ NOT in SARIF
+8. **CSS selector** per node (node.target) โ partial in `message.text` only
+9. **Learn more link** (violation.helpUrl) โ โ in `helpUri` (but broken for IBM)
+10. **Principle grouping** (violation.principle) โ โ NOT in SARIF properties
+
+---
+
+## 4. Missing SARIF Properties for GitHub Code Scanning Rich Display
+
+### Critical Missing Properties (Required by GitHub)
+
+#### `fullDescription.text` โ Required
+
+GitHub displays this alongside results. Currently absent; `shortDescription.text` is set to `violation.description` but `fullDescription` is not set at all.
+
+**Fix**: Set `fullDescription.text` to `violation.description` (same as shortDescription, or expand with WCAG criteria).
+
+#### `help.text` โ Required
+
+GitHub displays this as "Rule help" documentation. Without it, GitHub shows "no rule help available for this alert."
+
+**Fix**: Set `help.text` to `violation.help` โ the concise rule guidance.
+
+#### `help.markdown` โ Recommended (displayed instead of `help.text` when present)
+
+This is the key property for rich rule documentation in GitHub Code Scanning. When present, GitHub renders it as formatted markdown alongside alerts.
+
+**Recommended content for `help.markdown`**:
+
+```markdown
+## {violation.help}
+
+{violation.description}
+
+**Impact**: {violation.impact}
+**WCAG Criteria**: {wcag tags joined}
+**Principle**: {violation.principle}
+
+[Learn more]({violation.helpUrl})
+```
+
+### Important Missing Properties (Recommended by GitHub)
+
+#### `defaultConfiguration.level`
+
+Maps impact to SARIF level. Used by GitHub to establish default severity when `result.level` is not set.
+
+```typescript
+defaultConfiguration: {
+ level: mapImpactToLevel(violation.impact) // 'error' | 'warning' | 'note'
+}
+```
+
+#### `properties.precision`
+
+GitHub uses this with severity to order results. Accessibility scanner results are generally high confidence.
+
+```typescript
+properties: {
+ precision: 'high', // axe-core rules are well-tested
+ // or 'medium' for IBM potentialviolation
+}
+```
+
+#### `properties.problem.severity`
+
+Non-security result severity. Maps to impact:
+
+```typescript
+properties: {
+ 'problem.severity': violation.impact === 'critical' || violation.impact === 'serious'
+ ? 'error'
+ : violation.impact === 'moderate' ? 'warning' : 'recommendation'
+}
+```
+
+### Nice-to-Have Missing Properties
+
+#### `tool.driver.semanticVersion`
+
+GitHub prefers this over `version` for tracking tool version changes.
+
+#### `tool.driver.informationUri`
+
+Link to the tool's documentation/homepage.
+
+#### `automationDetails.id`
+
+Enables category-based analysis runs. Example: `"accessibility-scan/wcag2aa/"`.
+
+#### `relatedLocations`
+
+When a violation affects multiple nodes, only the first becomes the primary `location`. Additional nodes could be listed as `relatedLocations` with their own snippets and selectors.
+
+---
+
+## 5. Existing SARIF Examples in Codebase
+
+### Prior Research Example (from `.copilot-tracking/research/subagents/2026-03-06/cicd-integration-research.md`)
+
+Contains an ideal SARIF rule structure with all GitHub-supported fields:
+
+```json
+{
+ "id": "color-contrast",
+ "name": "color-contrast",
+ "shortDescription": { "text": "Elements must meet minimum color contrast ratio thresholds" },
+ "fullDescription": { "text": "Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds" },
+ "helpUri": "https://dequeuniversity.com/rules/axe/4.11/color-contrast",
+ "help": {
+ "text": "Elements must meet minimum color contrast ratio thresholds",
+ "markdown": "Elements must meet minimum color contrast ratio thresholds. [More info](https://dequeuniversity.com/rules/axe/4.11/color-contrast)"
+ },
+ "defaultConfiguration": { "level": "error" },
+ "properties": {
+ "tags": ["wcag2aa", "wcag143", "accessibility"],
+ "precision": "high",
+ "problem.severity": "error"
+ }
+}
+```
+
+### Test Fixture Example (from `sarif-generator.test.ts`)
+
+Test violations use axe-core help URLs which work fine:
+
+```typescript
+helpUrl: 'https://dequeuniversity.com/rules/axe/4.0/color-contrast'
+```
+
+### Raw IBM Result Example (from `results/https_/example.com.json`)
+
+IBM results contain full archive URLs in the `help` field:
+
+```json
+{
+ "ruleId": "style_color_misuse",
+ "help": "https://able.ibm.com/rules/archives/2026.03.04/doc/en-US/style_color_misuse.html#...",
+ "message": "Verify color is not used as the only visual means of conveying information",
+ "snippet": "