feat: AgentRC Readiness Scanner Web App#90
Conversation
|
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:
For more information about GitHub Code Scanning, check out the documentation. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
sendFilecallback callsnext()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();
});
});
There was a problem hiding this comment.
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.statusis interpolated directly into a class attribute (ai-criterion-icon ${c.status}) withininnerHTML. If a shared report contains a maliciousstatusvalue, this can lead to attribute injection/stored XSS. Constrainstatusto an allowlist (pass/fail/skip) before rendering, or build DOM nodes and setclassNamevia safe mapping.
webapp/frontend/src/report.js:338c.statusis interpolated directly intoclass="criterion-status ${c.status}"in multiple sections. Ifstatusis not strictly validated/normalized, this is an injection vector when usinginnerHTML. Recommend restrictingstatusto a known set and using a safe mapping for CSS classes.
There was a problem hiding this comment.
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 || 1will incorrectly coerce0to1, so shared reports withachievedLevel: 0won’t render accurately. Use a defaulting approach that preserves 0.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 upgradein the runtime image makes builds less reproducible and can unexpectedly change base packages over time. Prefer relying on the pinned base image plus explicitapk addpackages (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
danielmeppiel
left a comment
There was a problem hiding this comment.
🔍 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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
- 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.
…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
fix(cleanup): streamline error handling in removeTempDir function
…e naming fix(scanner): encode GitHub token in clone URL to prevent issues with special characters
AgentRC Readiness Scanner — Web Surface
A lightweight web application that exposes AgentRC readiness scanning through a browser interface. The webapp leverages
@agentrc/coreservices directly — importingcloneRepofrom@agentrc/core/services/gitandrunReadinessReportfrom@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
webapp/backend/webapp/frontend/infra/webapp/.github/workflows/webapp-ci.yml.github/workflows/webapp-cd.ymlDockerfile.webapptsxfor@agentrc/coreTypeScript imports at runtime.Architecture
The report renderer matches the CLI
readiness --htmloutput 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 installdoesn't installwebapp/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):eslint.config.js"webapp/**"to globalignores@typescript-eslint/parserwithparserOptions.project: "./tsconfig.json". Webapp's plain JS files aren't in the root TS project and would fail parsing.vitest.config.tsexclude: ["webapp/**", ...]totestconfigwebapp/backend/tests/*.test.jsbut can't resolve their imports (express,cors, etc.) since those deps live only inwebapp/backend/node_modules.tsconfig.json.prettierignorewebapp/andDockerfile.webappAll 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
productionenvironment):AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_SUBSCRIPTION_IDGH_TOKEN_FOR_SCANreposcope 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
webapp/,infra/webapp/, and workflow files@agentrc/coreimports — no wrapper, no abstraction layer; the scanner service calls core functions directlydocker-compose upfor local dev, CD pipeline for AzurecontainerStartupStrategyparameter (currently set tokeep-warm)