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": "