From 66c6c93997c792e4d8c2a08606e7a898818a43ac Mon Sep 17 00:00:00 2001 From: Patel230 Date: Mon, 1 Jun 2026 22:15:24 +0530 Subject: [PATCH 1/3] chore: add microservice architecture scaffolding Add Dockerfile, .dockerignore, .env.example, api/openapi.yaml, deploy/docker/docker-compose.yml, docs/architecture.md, and .github/workflows/docker.yml for GHCR build+push CI. --- .dockerignore | 12 +++ .env.example | 6 ++ .github/workflows/docker.yml | 63 ++++++++++++++ Dockerfile | 39 +++++++++ api/openapi.yaml | 81 ++++++++++++++++++ deploy/docker/docker-compose.yml | 13 +++ docs/architecture.md | 140 +++++++++++++++++++++++++++++++ 7 files changed, 354 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 api/openapi.yaml create mode 100644 deploy/docker/docker-compose.yml create mode 100644 docs/architecture.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..68d8ab7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +.github +.gitignore +*.md +.env +.env.* +Dockerfile +.dockerignore +coverage.out +docs/ +deploy/ +api/ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1826251 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# inspect environment variables — copy to .env and fill in +# LLM API key for AI-powered scanning (used by llm_scanner.go) +INSPECT_LLM_API_KEY= +INSPECT_LLM_PROVIDER=openai +# Chromium path override (optional — leave empty to use bundled chromium) +CHROME_PATH= diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..15c8ef6 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,63 @@ +name: Docker + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + branches: [main] + paths: + - "Dockerfile" + - "**.go" + - "go.mod" + - "go.sum" + +permissions: + contents: read + packages: write + +env: + REGISTRY: ghcr.io + IMAGE_NAME: graycodeai/inspect + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix=sha- + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + VERSION=${{ github.ref_name }} + COMMIT=${{ github.sha }} + BUILD_DATE=${{ github.event.head_commit.timestamp }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d28ffe4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM golang:1.26.3-alpine AS builder + +RUN apk add --no-cache git ca-certificates chromium + +WORKDIR /build +COPY go.mod go.sum ./ +RUN go mod download && go mod verify + +COPY . . +ARG VERSION=dev +ARG COMMIT=none +ARG BUILD_DATE=unknown + +RUN CGO_ENABLED=0 GOOS=linux go build -trimpath \ + -ldflags="-s -w \ + -X main.Version=${VERSION} \ + -X main.Commit=${COMMIT} \ + -X main.BuildDate=${BUILD_DATE}" \ + -o inspect-ci ./cmd/inspect-ci + +RUN CGO_ENABLED=0 GOOS=linux go build -trimpath \ + -ldflags="-s -w \ + -X main.Version=${VERSION} \ + -X main.Commit=${COMMIT} \ + -X main.BuildDate=${BUILD_DATE}" \ + -o inspect-action ./cmd/inspect-action + +FROM alpine:3.21 +# chromium is required for browser-based checks +RUN apk add --no-cache ca-certificates tini chromium && \ + adduser -D -u 1000 inspect + +COPY --from=builder /build/inspect-ci /usr/local/bin/inspect-ci +COPY --from=builder /build/inspect-action /usr/local/bin/inspect-action +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo + +USER inspect +ENTRYPOINT ["tini", "--", "inspect-ci"] +CMD ["--help"] diff --git a/api/openapi.yaml b/api/openapi.yaml new file mode 100644 index 0000000..364cd37 --- /dev/null +++ b/api/openapi.yaml @@ -0,0 +1,81 @@ +openapi: "3.1.0" +info: + title: inspect — Security Auditor Tool Reference + description: | + inspect is a website security, accessibility, and SEO auditor. + It operates as a CLI tool, GitHub Action, and MCP server (stdio). + + This document describes the MCP tool surface as a machine-readable reference. + inspect does NOT expose an HTTP server — all communication is via stdio MCP + or direct Go library calls. + version: "0.1.0" + license: + name: MIT + url: https://github.com/GrayCodeAI/inspect/blob/main/LICENSE + contact: + url: https://github.com/GrayCodeAI/inspect + +# No servers section — inspect has no HTTP API. +# MCP tools are documented below using x-mcp-tool extensions. + +tags: + - name: scan + description: Website scanning tools + - name: report + description: Report generation + +x-mcp-server: + transport: stdio + binary: inspect-ci + start_command: ["inspect-ci", "mcp"] + +x-mcp-tools: + inspect_scan: + description: Crawl a URL and run security, accessibility, SEO, and performance checks + inputSchema: + type: object + required: [url] + properties: + url: + type: string + description: Target URL to scan (must be http:// or https://) + checks: + type: array + items: + type: string + enum: [security, links, forms, a11y, performance, seo] + description: Checks to run (default: all) + depth: + type: integer + default: 3 + description: Maximum crawl depth + concurrency: + type: integer + default: 5 + description: Number of concurrent crawlers + format: + type: string + enum: [text, json, sarif, html, markdown, junit] + default: json + fail_on: + type: string + enum: [info, low, medium, high, critical] + description: Minimum severity that counts as a failure + + inspect_scan_dir: + description: Scan a local directory of HTML files + inputSchema: + type: object + required: [path] + properties: + path: + type: string + description: Absolute path to the HTML directory + checks: + type: array + items: + type: string + format: + type: string + enum: [text, json, sarif, html, markdown] + default: json diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml new file mode 100644 index 0000000..ee70f3e --- /dev/null +++ b/deploy/docker/docker-compose.yml @@ -0,0 +1,13 @@ +name: inspect + +services: + inspect: + build: + context: ../../ + dockerfile: Dockerfile + image: ghcr.io/graycodeai/inspect:dev + env_file: + - path: ../../.env.example + required: false + entrypoint: ["inspect-ci"] + command: ["--help"] diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..b4613af --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,140 @@ +
+ +# 🔍 inspect Architecture + +**Website Security, Accessibility & SEO Auditor** + +[![Go](https://img.shields.io/badge/Go-1.26+-00ADD8?logo=go)](https://go.dev/) +[![Protocol](https://img.shields.io/badge/Protocol-MCP-purple)]() + +
+ +--- + +## 🎯 Overview + +inspect is a website security auditing and crawling library for Go. It crawls sites concurrently, runs **security**, **accessibility**, **SEO**, and **performance** checks, and generates findings with severity levels and **CWE references**. + +> 💡 Three modes: **Go library**, **CLI binary** (`inspect-ci`), and **GitHub Action**. + +--- + +## 🧱 Components + +``` +inspect/ +├── api/openapi.yaml 📜 MCP tool surface reference +├── cmd/ +│ ├── inspect-ci/main.go 🖥️ CLI binary entry point +│ └── inspect-action/main.go ⚡ GitHub Action entry point +├── inspect.go 📤 Public API: Scan(), Finding, Report, Stats +├── check.go 🛡️ Checker interface, RuleCheck, RegisterCheck() +├── scanner.go 🔄 Crawler orchestration, check execution +├── options.go ⚙️ config, With* functions, presets +├── config.go 📋 .inspect.toml loading +├── sarif.go 📊 SARIF output formatter +├── ci_output.go 🖥️ CI-friendly terminal output +├── llm_scanner.go 🤖 AI-powered scanning +├── api_security.go 🔒 API endpoint security checks +├── dependency_check.go 📦 Dependency vulnerability checks +├── sbom.go 📋 SBOM generation +├── browser.go 🌐 Browser automation entry +├── browser/ 🖥️ Rod-based browser crawling +├── checks/ +│ ├── security.go 🔒 CSP, HSTS, CORS headers +│ ├── accessibility.go ♿ ARIA violations +│ ├── tls.go 🔐 Certificate checks +│ ├── cookies.go 🍪 Cookie security flags +│ ├── headers.go 📋 Missing security headers +│ └── mixed_content.go ⚠️ Mixed content detection +├── mcp/ 🔌 MCP server (stdio transport) +└── internal/ + ├── crawler/ 🕷️ URL parsing, sitemap, robots.txt + ├── check/ 🔄 Internal check runner + ├── html/ 📄 HTML parsing utilities + └── report/ 📊 Output format implementations +``` + +--- + +## 📤 Public API + +```go +// 🚀 One-shot scan +report, err := inspect.Scan(ctx, "https://example.com", + inspect.WithChecks("security", "a11y"), + inspect.WithDepth(3), +) + +// 🔄 Reusable scanner +scanner := inspect.NewScanner(inspect.WithConcurrency(10)) +report, err := scanner.Scan(ctx, "https://example.com") + +// 🛡️ Custom Go check +inspect.RegisterCheck(myCheck) + +// 📋 Declarative rule (no Go code) +inspect.RegisterRule(inspect.RuleCheck{ + Name: "x-frame-options", Severity: inspect.High, + Check: inspect.HeaderMissing{Header: "X-Frame-Options"}, +}) +``` + +--- + +## ⚡ Presets + +| Preset | Checks | Speed | +|--------|--------|:-----:| +| 🏃 `Quick` | links, security headers | Fast | +| 📊 `Standard` | links, security, forms, a11y | Medium | +| 🔬 `Deep` | all checks, depth 10 | Slow | +| 🔒 `SecurityOnly` | security, TLS, cookies, headers | Fast | +| 🤖 `CI` | all checks, fail on Medium+ | Medium | + +--- + +## 🔌 MCP Server + +```bash +inspect-ci mcp # 📡 stdio transport (add to agent MCP config) +``` + +**Tools:** `inspect_scan` — crawl URL and run checks · `inspect_scan_dir` — scan local HTML directory + +--- + +## 🐙 GitHub Action + +```yaml +- uses: GrayCodeAI/inspect@v0.4.0 + with: + url: https://example.com + checks: security,a11y + fail-on: high +``` + +--- + +## 🔎 Findings + +Each finding includes: + +| Field | Description | +|-------|-------------| +| `Check` | Which check produced this finding | +| `Severity` | 🟢 Info · 🟡 Low · 🟠 Medium · 🔴 High · 🟥 Critical | +| `URL` | Page where the issue was found | +| `Message` | Human-readable description | +| `Evidence` | Snippet of the problematic content | +| `CWE` | CWE reference (required for security findings) | +| `Confidence` | 0.0–1.0 score | + +--- + +## 🛡️ ReDoS Protection + +All user-supplied regex patterns go through: +- `compileWithTimeout()` — **1s** compilation limit +- `matchWithTimeout()` — **100ms** match limit +- `checkRegexComplexity()` — rejects nested quantifiers before compilation From cacfbe376406df20216009d5b00441d577e99675 Mon Sep 17 00:00:00 2001 From: Patel230 Date: Mon, 1 Jun 2026 22:54:57 +0530 Subject: [PATCH 2/3] fix: exclude go.work from Docker build context go.work contains local replace directives that break Docker builds. Add go.work and go.work.sum to .dockerignore. --- .dockerignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.dockerignore b/.dockerignore index 68d8ab7..ee75bcb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,3 +10,5 @@ coverage.out docs/ deploy/ api/ +go.work +go.work.sum From 80a852760f1abe6f87a873321c7069977e9131fe Mon Sep 17 00:00:00 2001 From: Patel230 Date: Mon, 1 Jun 2026 23:23:33 +0530 Subject: [PATCH 3/3] fix: add tzdata to builder stage for zoneinfo copy The final stage copies /usr/share/zoneinfo from the builder but golang:alpine doesn't include tzdata by default. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d28ffe4..06a6958 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM golang:1.26.3-alpine AS builder -RUN apk add --no-cache git ca-certificates chromium +RUN apk add --no-cache git ca-certificates chromium tzdata WORKDIR /build COPY go.mod go.sum ./