Skip to content

feat: AgentRC Readiness Scanner Web App#90

Open
webmaxru wants to merge 34 commits intomicrosoft:mainfrom
webmaxru:webapp
Open

feat: AgentRC Readiness Scanner Web App#90
webmaxru wants to merge 34 commits intomicrosoft:mainfrom
webmaxru:webapp

Conversation

@webmaxru
Copy link
Copy Markdown

AgentRC Readiness Scanner — Web Surface

A lightweight web application that exposes AgentRC readiness scanning through a browser interface. The webapp leverages @agentrc/core services directly — importing cloneRepo from @agentrc/core/services/git and runReadinessReport from @agentrc/core/services/readiness — to be as minimally invasive and standalone as possible. No core logic is duplicated; the webapp is a thin HTTP + UI layer on top of the existing engine.

Live App

https://agentrc-webapp.redsand-b56d7588.eastus.azurecontainerapps.io

What's Included

Area Files Description
Backend webapp/backend/ Express server with scan, config, and report-sharing routes. Rate-limited, Helmet-secured, health-checked.
Frontend webapp/frontend/ Vanilla JS SPA — scan form, progress UI, CLI-matching report renderer with dark/light theme.
Infrastructure infra/webapp/ Bicep templates for Azure Container Apps (Consumption plan), Log Analytics, App Insights, Storage.
CI .github/workflows/webapp-ci.yml PR checks — lint, typecheck, unit tests (58 tests).
CD .github/workflows/webapp-cd.yml Build → GHCR push → Trivy scan → Bicep deploy → smoke test.
Docker Dockerfile.webapp Multi-stage Node 24 Alpine build. Uses tsx for @agentrc/core TypeScript imports at runtime.

Architecture

Browser → Express API → @agentrc/core (git clone + readiness scan) → JSON report → Frontend renderer

The report renderer matches the CLI readiness --html output format — same pillars, maturity levels, and color coding.

Root CI Compatibility

The webapp is designed to be standalone, but the root CI workflows (ESLint, Prettier, vitest) automatically pick up all files in the repo. Since root npm install doesn't install webapp/backend/node_modules, webapp files would fail in root CI. These minimal changes to root config files ensure the root CI ignores the webapp directory (the webapp has its own dedicated CI workflow):

File Change Reason
eslint.config.js Added "webapp/**" to global ignores Root ESLint uses @typescript-eslint/parser with parserOptions.project: "./tsconfig.json". Webapp's plain JS files aren't in the root TS project and would fail parsing.
vitest.config.ts Added exclude: ["webapp/**", ...] to test config Root vitest would discover webapp/backend/tests/*.test.js but can't resolve their imports (express, cors, etc.) since those deps live only in webapp/backend/node_modules.
tsconfig.json No net change (was temporarily added, then reverted) Webapp doesn't need to be in the root TypeScript project.
.prettierignore Added webapp/ and Dockerfile.webapp Prettier can't parse Dockerfiles (syntax error). Webapp frontend files have their own formatting conventions.

All root CI checks pass: Lint & Format, Typecheck, Build & Verify, and all 3 test matrices (Node 20/22, Ubuntu/Windows).

Repository Setup Required

To deploy your own instance, configure these GitHub repository secrets (in a production environment):

Secret Description
AZURE_CLIENT_ID Azure AD app registration client ID (OIDC federated credential)
AZURE_TENANT_ID Azure AD tenant ID
AZURE_SUBSCRIPTION_ID Target Azure subscription
GH_TOKEN_FOR_SCAN GitHub PAT for cloning repos during scans (needs repo scope for private repos, or just public access)

The Azure AD app needs Contributor role on the subscription and a federated credential for repo:<owner>/<repo>:environment:production.

Key Design Decisions

  • Zero core changes — all new files live under webapp/, infra/webapp/, and workflow files
  • Minimal root config touches — only ignore/exclude patterns added so root CI skips the webapp directory
  • Direct @agentrc/core imports — no wrapper, no abstraction layer; the scanner service calls core functions directly
  • Standalone deployabledocker-compose up for local dev, CD pipeline for Azure
  • Report sharing — optional Azure File Share–backed persistent storage for shareable report URLs
  • Scale-to-zero or keep-warm — configurable via Bicep containerStartupStrategy parameter (currently set to keep-warm)

@webmaxru webmaxru requested a review from pierceboggan as a code owner March 31, 2026 09:15
Copilot AI review requested due to automatic review settings March 31, 2026 09:15
@webmaxru webmaxru requested a review from digitarald as a code owner March 31, 2026 09:15
@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Introduces a standalone “AgentRC Readiness Scanner” web surface (Express backend + vanilla JS frontend) that runs readiness scans via @agentrc/core, with supporting Docker, Azure Container Apps infra (Bicep), and dedicated CI/CD workflows. Root-level lint/test config is adjusted to ignore webapp/** so the existing repo CI doesn’t attempt to parse or run webapp-only dependencies.

Changes:

  • Added Express backend routes for scanning repos, serving config/health, and optional report sharing with storage + validation.
  • Added a static frontend SPA (HTML/CSS/JS) that handles repo routing/history and renders a CLI-like readiness report (with optional share UX).
  • Added Dockerfile, docker-compose, Azure infra templates, and GitHub Actions workflows for webapp CI/CD; updated root ESLint/Vitest/Prettier ignores.

Reviewed changes

Copilot reviewed 37 out of 38 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
webapp/frontend/tests/repo-location.test.js Unit tests for frontend repo URL/path parsing helpers.
webapp/frontend/src/report.js Frontend report renderer (HTML generation, share button, service info).
webapp/frontend/src/repo-location.js Frontend repo reference parsing + browser history sync helpers.
webapp/frontend/src/main.css Web UI styling (theme, layout, report visuals).
webapp/frontend/src/app.js Frontend orchestration: routing, scan execution, progress/error UI, theme toggle.
webapp/frontend/src/api.js Frontend HTTP client for backend API endpoints.
webapp/frontend/index.html SPA shell, scan form, inline theme init, static asset wiring.
webapp/docker-compose.yml Local container run configuration (env + volume for reports).
webapp/backend/vitest.config.js Backend Vitest configuration.
webapp/backend/tests/url-parser.test.js Tests for backend SSRF-safe GitHub URL parser.
webapp/backend/tests/storage.test.js Tests for report storage (memory mode).
webapp/backend/tests/routes.test.js Contract tests for API routes (scan/config/health/report).
webapp/backend/tests/report-validator.test.js Tests for shared report normalization/validation.
webapp/backend/tests/cleanup.test.js Tests for temp directory creation/removal utilities.
webapp/backend/src/utils/url-parser.js SSRF-safe GitHub URL parsing + validation errors.
webapp/backend/src/utils/cleanup.js Temp dir lifecycle + stale temp sweep utilities.
webapp/backend/src/services/storage.js File/memory report persistence with TTL cleanup.
webapp/backend/src/services/scanner.js Clone + readiness scan orchestration, concurrency limits, stale sweep timer.
webapp/backend/src/services/report-validator.js Shared report validation/normalization before persisting.
webapp/backend/src/server.js Express app factory + runtime config + static frontend serving.
webapp/backend/src/routes/scan.js /api/scan endpoint: validate input and run scan.
webapp/backend/src/routes/report.js /api/report endpoints: save and fetch shared reports.
webapp/backend/src/routes/config.js /api/config endpoint: expose public runtime config to frontend.
webapp/backend/src/middleware/rate-limiter.js Rate limiting for scan/report endpoints.
webapp/backend/src/middleware/error-handler.js Centralized error → HTTP status mapping and safe responses.
webapp/backend/package.json Backend package metadata, scripts, and dependencies.
webapp/.env.example Example env vars for local dev and sharing configuration.
vitest.config.ts Root Vitest excludes webapp/** to avoid discovering webapp tests/deps.
infra/webapp/main.json Generated ARM template for Azure Container Apps deployment.
infra/webapp/main.bicepparam Default parameter file for deploying the webapp infra.
infra/webapp/main.bicep Bicep source for Azure infra (ACA, storage, insights, logs).
eslint.config.js Root ESLint ignores webapp/**.
Dockerfile.webapp Multi-stage build for a single-container deployment (backend + static frontend).
.prettierignore Ignores webapp/ and Dockerfile.webapp in root formatting checks.
.github/workflows/webapp-ci.yml Webapp-scoped CI: backend tests + Docker build + Trivy scan.
.github/workflows/webapp-cd.yml Webapp CD: build/push image + scan + deploy via Bicep + smoke test.
.github/prompts/plan-agentrcWebapp.prompt.md Design/plan prompt documenting the intended architecture and steps.
Files not reviewed (1)
  • webapp/backend/package-lock.json: Language not supported

Copilot AI review requested due to automatic review settings March 31, 2026 09:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Files not reviewed (1)
  • webapp/backend/package-lock.json: Language not supported
Comments suppressed due to low confidence (3)

webapp/backend/src/server.js:67

  • The CSP explicitly allows 'unsafe-inline' scripts/styles. Since this app already ships module scripts, consider removing 'unsafe-inline' by moving the inline theme bootstrap into a static JS file (or using nonces) to keep CSP effective against XSS.
  // Security headers
  app.use(
    helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          scriptSrc: ["'self'", "'unsafe-inline'"],
          styleSrc: ["'self'", "'unsafe-inline'"],
          imgSrc: ["'self'", "data:"],
          connectSrc: ["'self'"]
        }
      }
    })

webapp/backend/src/server.js:72

  • app.use(cors()) enables CORS for all origins by default. If the API is intended to be same-origin only (or limited to a known set of domains), configure allowed origins explicitly to reduce the risk of third-party sites driving scans from browsers.

  app.use(cors());
  app.use(express.json({ limit: "1mb" }));

webapp/backend/src/server.js:94

  • In the SPA catch-all route, the sendFile callback calls next() when an error occurs. This drops the underlying error and can result in confusing 404s or partially-sent responses. Pass the error through (next(err)) so it reaches the error handler correctly.
  // SPA catch-all: serve index.html for non-API routes
  app.get(/^\/(?!api\/).*/, (_req, res, next) => {
    res.sendFile("index.html", { root: runtime.frontendPath }, (err) => {
      if (err) next();
    });
  });

Copilot AI review requested due to automatic review settings March 31, 2026 10:47
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Files not reviewed (2)
  • webapp/backend/package-lock.json: Language not supported
  • webapp/frontend/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)

webapp/frontend/src/report.js:224

  • c.status is interpolated directly into a class attribute (ai-criterion-icon ${c.status}) within innerHTML. If a shared report contains a malicious status value, this can lead to attribute injection/stored XSS. Constrain status to an allowlist (pass/fail/skip) before rendering, or build DOM nodes and set className via safe mapping.
    webapp/frontend/src/report.js:338
  • c.status is interpolated directly into class="criterion-status ${c.status}" in multiple sections. If status is not strictly validated/normalized, this is an injection vector when using innerHTML. Recommend restricting status to a known set and using a safe mapping for CSS classes.

Copilot AI review requested due to automatic review settings March 31, 2026 11:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Files not reviewed (2)
  • webapp/backend/package-lock.json: Language not supported
  • webapp/frontend/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)

webapp/frontend/src/report.js:278

  • Same issue as the hero section: report.achievedLevel || 1 will incorrectly coerce 0 to 1, so shared reports with achievedLevel: 0 won’t render accurately. Use a defaulting approach that preserves 0.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 38 out of 40 changed files in this pull request and generated 5 comments.

Files not reviewed (2)
  • webapp/backend/package-lock.json: Language not supported
  • webapp/frontend/package-lock.json: Language not supported

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Files not reviewed (2)
  • webapp/backend/package-lock.json: Language not supported
  • webapp/frontend/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)

Dockerfile.webapp:24

  • Running apk upgrade in the runtime image makes builds less reproducible and can unexpectedly change base packages over time. Prefer relying on the pinned base image plus explicit apk add packages (or pin package versions) rather than upgrading everything during build.
ENV REPORTS_DIR=/app/data/reports
RUN apk upgrade --no-cache \
  && mkdir -p /app/data /tmp/agentrc-scans \
  && chown -R node:node /app /tmp/agentrc-scans

Copy link
Copy Markdown
Contributor

@danielmeppiel danielmeppiel left a comment

Choose a reason for hiding this comment

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

🔍 Expert Panel Review — Preview Launch Readiness

Great contribution, @webmaxru. The core integration pattern here is exactly right — two imports into @agentrc/core, zero core changes, automatic benefit propagation (the APM criteria from PR #92 already surface through this webapp). The thin-wrapper design mirrors how the CLI and VS Code extension consume core, and the infra/CI/CD completeness is above what we typically see in a first PR.

We ran this through a structured review with three specialist perspectives (UX/DevEx, OSS evaluator tooling, software architecture). All three agreed on the strengths and converged on a short list of changes needed before we can ship this as a preview.


Context: Why These Changes

agentrc is early-stage and we want this surface out fast — the AI readiness scanning niche has zero competition and speed matters. But we also need strong fundamentals that won't require rewrites when we scale. The changes below are the minimum set that balances KISS with "won't bite us later."


Required for Preview (4 items, ~4 hours total)

1. Eliminate tsx from production runtime (~1-2 hours)

The Dockerfile runs node --import tsx src/server.js, making tsx a production dependency for runtime TypeScript transpilation of @agentrc/core. This means:

  • Core API breakages are caught at runtime in production, not build time (the CLI and VS Code extension both catch these at build time via tsup/esbuild)
  • Cold-start latency includes transpilation overhead
  • tsx + esbuild become part of the production attack surface

Ask: Add a small esbuild/tsup build step for webapp/backend that bundles the core imports at build time. This eliminates the Docker symlink hack (ln -sf /app/packages/core node_modules/@agentrc/core) and aligns with how both other surfaces consume core. The Dockerfile CMD becomes node dist/server.js.

2. Replace fake progress bar with honest loading state (~1 hour)

The hardcoded 15% → 45% → 90% progression in executeScan() doesn't reflect actual backend progress — the bar shows 45% before the fetch even resolves. For large repos (30-60s clone), users watch a frozen progress bar. This actively erodes trust.

Ask: Replace with an indeterminate spinner + stage text ("Cloning repository…" → "Analyzing…" → "Done"). No SSE/WebSocket needed — just stop lying about percentage. An honest spinner sets no false expectations.

3. Stop exposing App Insights connection string on /api/config (~10 min)

// webapp/backend/src/routes/config.js
res.json({
  appInsightsConnectionString: runtime.appInsightsConnectionString || null
});

This allows anyone to inject telemetry into the project's App Insights instance. The frontend doesn't need this from the API — if client-side telemetry is desired, inject it at build time or via a <meta> tag.

Ask: Remove appInsightsConnectionString from the config endpoint response.

4. Replace ACR admin user with managed identity (~30 min)

The Bicep template enables adminUserEnabled: true and passes static credentials as Container App secrets. This is explicitly discouraged by Azure best practices — admin credentials are shared, non-auditable, and can't be scoped.

Ask: Use system-assigned managed identity on the Container App with AcrPull role assignment:

identity: { type: 'SystemAssigned' }
registries: [{ server: acr.properties.loginServer, identity: 'system' }]

Recommended for Fast-Follow (not blocking preview)

These are tracked but don't need to gate the preview:

# Item Why it can wait
1 Convert backend to TypeScript Plain JS works for now; the build step (item 1 above) gives us build-time safety on the core boundary
2 Add --no-recurse-submodules to clone Low probability attack vector behind rate limiting; add as hardening pass
3 Structured logging (pino/winston) console.log is fine for preview scale; upgrade when we add monitoring
4 ARIA live regions + focus management Important for GA, acceptable gap for preview
5 Remove unsafe-inline from CSP Low-risk with current self-only CSP; harden for GA
6 Zero-downtime deployments Acceptable for preview with low traffic
7 README badge endpoint (/badge/:owner/:repo) Highest-leverage growth feature — prioritize for first fast-follow

What We Love

  • Zero core changes — exemplary contribution pattern
  • Automatic criteria propagation — APM checks, VS Code location settings, everything new just works
  • Complete package — infra, CI/CD, Docker, tests, all shipped together
  • Security fundamentals — SSRF-safe URL parsing, credential stripping, prototype pollution protection, report validation
  • 58 tests with 270 lines on the report validator alone

Looking forward to getting this out as a preview. The positioning is strong — let's get the four items above addressed and ship it. 🚀

… and criteria arrays

fix(report): update report rendering logic to handle edge cases in passed and total values
style(progress): replace progress bar with spinner for better UX during repository cloning
fix(config): remove appInsightsConnectionString from public config response
fix(Dockerfile): ensure core package symlink is recreated after removal
fix(bicep): disable admin user for Azure Container Registry and add AcrPull role assignment
chore(package-lock): update dependencies and remove unnecessary dev dependencies
Copilot AI review requested due to automatic review settings March 31, 2026 21:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Files not reviewed (2)
  • webapp/backend/package-lock.json: Language not supported
  • webapp/frontend/package-lock.json: Language not supported

fix: resolve frontend path using fileURLToPath for better compatibility
fix: enhance theme toggle functionality to handle localStorage errors gracefully
Copilot AI review requested due to automatic review settings March 31, 2026 21:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 42 changed files in this pull request and generated 1 comment.

Files not reviewed (2)
  • webapp/backend/package-lock.json: Language not supported
  • webapp/frontend/package-lock.json: Language not supported

webmaxru added 2 commits April 1, 2026 00:02
- Changed the start script to run the bundled server from the dist directory.
- Added a build script to bundle the application using esbuild.
- Introduced a new esbuild configuration file to handle the bundling of the server.
- Updated dependencies to include esbuild and adjusted the location of @agentrc/core.
Copilot AI review requested due to automatic review settings March 31, 2026 22:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Files not reviewed (1)
  • webapp/frontend/package-lock.json: Language not supported

webmaxru added 2 commits April 1, 2026 00:16
…a fields to prevent XSS

fix(report): use safe number handling for app and area summaries in report rendering
fix(Dockerfile): include node_modules from deps for backend build
Copilot AI review requested due to automatic review settings March 31, 2026 22:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 42 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • webapp/frontend/package-lock.json: Language not supported

Copilot AI review requested due to automatic review settings March 31, 2026 22:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 41 out of 43 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • webapp/frontend/package-lock.json: Language not supported

…e naming

fix(scanner): encode GitHub token in clone URL to prevent issues with special characters
@webmaxru webmaxru requested a review from danielmeppiel March 31, 2026 22:43
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.

4 participants