Skip to content

Add security-first PR sweeper + fix audit critical findings - #54

Draft
soyalejolopez wants to merge 10 commits into
masterfrom
alejanl-microsoft-repo-audit-pr-sweeper
Draft

Add security-first PR sweeper + fix audit critical findings#54
soyalejolopez wants to merge 10 commits into
masterfrom
alejanl-microsoft-repo-audit-pr-sweeper

Conversation

@soyalejolopez

Copy link
Copy Markdown
Owner

Summary

Adds an intelligent, security-first PR sweeper to help maintain the repo, and fixes the critical findings from a full repo + GitHub Pages audit.

This PR makes it easy to contribute (clear automated feedback on every PR) while keeping the repo secure by default (deterministic guardrails + hardened, pwn-request-safe CI).

1. PR Sweeper automation

A two-stage, pwn-request-safe design:

  • Stage 1 — Analyze (pull_request, untrusted): contents: read only, no secrets, never executes PR-authored code. Captures diff/numstat/name-status/sizes as an artifact.
  • Stage 2 — Report (workflow_run, trusted): holds the write token + models:read, consumes the Stage-1 artifact as data only, never checks out PR code. Config/policy always loaded from the trusted base checkout.

What it does:

  • Deterministic guardrails (authoritative gate): secret + PII scans on added lines, file-type policy, blocked extensions, sensitive-path detection, scope checks. Only a true block fails the commit status.
  • Advisory dual-model AI review via GitHub Models (openai/gpt-5 + openai/o3), injection-resistant, fail-open — it can never flip the deterministic gate. (Azure AI Foundry path stubbed for a Claude/Opus reviewer.)
  • Posts one sticky comment, applies sweeper:* labels, sets a commit status.
  • Governance: CODEOWNERS, dependabot.yml, SHA-pinned actions.

Independently security-reviewed by a second model (7 findings, all fixed): removed a shell-injection sink, bound Stage 2 to the trusted workflow_run head commit (anti-spoof), hunk-aware secret scanning, full secret redaction, uniform output sanitization.

Note: workflow_run automations only take effect once they exist on the default branch — expected behavior.

2. Audit critical fixes

  • Stored XSS in catalog rendering — untrusted metadata no longer reaches innerHTML.
  • Message-center script injection + auto-merge — safe serialization of upstream data; removed auto-merge of externally-derived content.
  • Broken detail-page tag links — no longer throws; sets filter state correctly.
  • Fabricated engagement metrics — synthetic views/upvotes removed; real stats only.
  • Potential tenant data in sample CSVs — replaced with synthetic Contoso identities + reserved example numbers/GUIDs.
  • Workflow hardening — least-privilege permissions, SHA-pinned actions.

Remaining backlog (not in this PR)

120 broken relative doc links + link-checker, additional governance files (CODE_OF_CONDUCT.md, SUPPORT.md, issue forms, .editorconfig, .gitattributes), CodeQL, PAT scoping/concurrency, accessibility polish, catalog schema (Ajv) hardening.

— Iceman 🛩️, your wingman

soyalejolopez and others added 4 commits July 21, 2026 08:11
#48)

Promote the new Linear-style resource catalog to the site root (index.html)
and introduce a front-matter -> catalog.json -> site data pipeline so
contributions flow efficiently into both the repo and the page.

Site (index.html)
- Modern, filterable catalog: comfortable + compact (table) densities,
  whole-card navigation, per-resource detail pages (what it is / why use it /
  how to use / prerequisites / author / version).
- Engagement layer: views + upvotes with Popular / Trending / New / Engaging
  sorts. Microsoft branding, service description + contact links.
- Microsoft Clarity analytics preserved; reduced-motion respected.

Metadata pipeline
- YAML front-matter in each resource README (authorship + semver versioning;
  git history is authoritative).
- tools/catalog-build generator produces catalog.json (schema in
  docs/CATALOG-METADATA.md); the site fetches it at runtime.
- .github/workflows/build-catalog.yml validates front-matter on PRs and
  regenerates catalog.json on push to master.
- CONTRIBUTING.md + TEMPLATE-README.md updated with the new requirements.
- Seeded front-matter across 30 resources.


Copilot-Session: b0067184-a71b-474a-98d0-6d8009659085

Co-authored-by: soyalejolopez <88358406+soyalejolopez@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
#49)

The 'Commit generated catalog' step failed on push to master with
'fatal: pathspec design-concepts/catalog.json did not match any files'
because the design-concepts/ prototype folder is intentionally not part
of the public repo.

- Workflow: only stage/commit the root catalog.json; add a paths filter to
  the push trigger so publish runs only on catalog-relevant changes; add
  [skip ci] to the bot commit.
- Generator: derive generatedAt from the newest resource 'updated' date
  instead of build time, so catalog.json is deterministic and the publish
  job only commits when resource content actually changes (no churn/loops).
- Regenerated catalog.json (30 resources).


Copilot-Session: b0067184-a71b-474a-98d0-6d8009659085

Co-authored-by: soyalejolopez <88358406+soyalejolopez@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: c70dfe8d-b7cb-4fba-8792-1b07695e7c93

Co-authored-by: soyalejolopez <88358406+soyalejolopez@users.noreply.github.com>
Two-stage, fork-safe (pwn-request-hardened) PR review:
- Stage 1 (pull_request, untrusted): contents:read only, no secrets, never
  runs PR code; captures diff + file metadata as an artifact.
- Stage 2 (workflow_run, trusted): deterministic guardrails (authoritative
  secret/PII/file-policy/scope gate) + advisory dual-model AI review via
  GitHub Models, then one sticky comment, sweeper:* labels, commit status.

Security properties:
- Write targets (PR number, head SHA) come from the trusted workflow_run
  event, never the attacker-controlled artifact, so a crafted artifact can't
  retarget another PR/commit.
- No PR-body interpolation in Stage 1 (removed the shell-injection sink).
- Hunk-aware diff parser so header-lookalike added lines can't bypass the
  secret scan; deletions of blocked binaries no longer fail the gate.
- All model/PR-derived text sanitized before entering the bot comment;
  secret evidence fully redacted.
- Least privilege per job; actions pinned to full SHAs; Dependabot + CODEOWNERS.

Reviewed by both Opus 4.8 (author) and GPT 5.6 Sol (independent security pass).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cecb163d-04c4-4b01-ab7a-14d25f7ec690
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

AnkitaDudeja and others added 6 commits July 21, 2026 20:19
- Escape all catalog-derived strings before DOM interpolation; sanitize markdown link URLs against an http(s)/relative allowlist (reject javascript:).
- Add defense-in-depth validation in catalog-build: string length caps, reject raw </> in plain-text fields, URL allowlist.
- Replace broken inline tag-link handler with real DOM buttons that set filter state (no more null.click TypeError).
- Remove synthetic views/upvotes; load real resource-stats.json, show '-' when absent, degrade Popular/Trending/Engaging sorts gracefully.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cecb163d-04c4-4b01-ab7a-14d25f7ec690
- update-message-center.js: safe inline-script serialization (JSON.stringify + escape < > & U+2028/9) and schema whitelist of upstream merill/mc fields; drop auto-merge of externally-derived content (human review required).
- Sanitize sample CSVs to synthetic Contoso identities, reserved 555-01xx numbers, randomized GUIDs (headers/format preserved).
- build-catalog.yml: default contents:read, grant contents:write only to publish job.
- SHA-pin all editable workflow actions (checkout/setup-node/etc.).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cecb163d-04c4-4b01-ab7a-14d25f7ec690
- WCAG AA contrast for --text-lighter (light + dark); aria-pressed on category/type/tag filters; dynamic theme-toggle label; skip link; single <main> landmark.
- Theme init/binding now runs unconditionally (works during catalog outage); script totals derived from catalog instead of hardcoded; corrected hero claim.
- Added meta description, canonical, Open Graph + Twitter tags; footer analytics/privacy disclosure for Clarity.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cecb163d-04c4-4b01-ab7a-14d25f7ec690
- Governance: CODE_OF_CONDUCT.md, SUPPORT.md, modern issue forms (bug/feature/config); remove legacy ISSUE_TEMPLATE.md; add .editorconfig + .gitattributes; refresh SECURITY.MD to current MSRC template.
- CI: add CodeQL (javascript-typescript) and markdown link-check workflows (SHA-pinned); add concurrency + timeout-minutes to scheduled/build workflows; document PAT->GitHub App recommendation.
- Catalog: JSON Schema + Ajv validation, duplicate-slug rejection, chronology/future-date checks, real generatedAt timestamp, and a legacy-exclusions manifest so new metadata-less resources fail the build.
- Polish: canonical lowercase microsoft URLs, expanded .gitignore, CHANGELOG entry.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cecb163d-04c4-4b01-ab7a-14d25f7ec690
Canonicalize ~122 broken license/issue links across 59 script READMEs to absolute microsoft/FastTrack URLs, plus fix stale/template links (e.g. copilot-analytics-samples index). Link-check CI (added separately) guards against regressions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cecb163d-04c4-4b01-ab7a-14d25f7ec690
The "Write PR metadata" step warned that interpolating the PR body into a
shell step is an injection sink, but it wrote the pull_request.body context
expression (in ${{ }} form) inside a comment to say so. GitHub expands ${{ }}
expressions textually before bash runs -- even inside a "#" comment -- so the
PR body was injected into the generated script. On any PR whose body contained
newlines this broke the step with exit code 127 ("$'\r': command not found").

Rewritten as literal "pull_request.body" text so nothing is expanded. The body
is still deliberately never captured; all values continue to flow through env
and jq --arg.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cecb163d-04c4-4b01-ab7a-14d25f7ec690
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants