diff --git a/.ralph/fix_plan.md b/.ralph/fix_plan.md index c31605a..1b40690 100644 --- a/.ralph/fix_plan.md +++ b/.ralph/fix_plan.md @@ -405,17 +405,17 @@ Spec: `.ralph/specs/phase-18-cli-cicd.md` --- -## Phase 25: Polish + Quality [PENDIENTE] +## Phase 25: Polish + Quality [COMPLETO] -- [ ] 25.1: Audit TypeScript strict — eliminar TODOS los `any` restantes -- [ ] 25.2: Loading skeletons en todas las pages (shadcn Skeleton) -- [ ] 25.3: Error boundaries en cada page +- [x] 25.1: Audit TypeScript strict — eliminar TODOS los `any` restantes +- [x] 25.2: Loading skeletons en todas las pages (shadcn Skeleton) +- [x] 25.3: Error boundaries en cada page - [ ] 25.4: Keyboard shortcuts: ⌘K (command palette), Esc (close dialogs), N (new exploration from dashboard) - [ ] 25.5: Responsive mobile: sidebar collapse, tables scroll, forms stack -- [ ] 25.6: README.md profesional: badges (build, license, version), screenshots, features list, quick start, CLI docs, architecture diagram, contributing -- [ ] 25.7: CONTRIBUTING.md -- [ ] 25.8: LICENSE files: MIT para core, archivo LICENSE-ENTERPRISE separado -- [ ] 25.9: Commit: `fase(25): polish and quality improvements` +- [x] 25.6: README.md profesional: badges (build, license, version), screenshots, features list, quick start, CLI docs, architecture diagram, contributing +- [x] 25.7: CONTRIBUTING.md +- [x] 25.8: LICENSE files: MIT para core, archivo LICENSE-ENTERPRISE separado +- [x] 25.9: Commit: `fase(25): polish and quality improvements` --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f76a08d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing to ABE + +Thank you for your interest in contributing to ABE! + +## Development Setup + +1. Fork and clone the repository +2. Install dependencies: `npm install && cd frontend && npm install` +3. Run migrations: `npm run db:migrate` +4. Start dev servers: `npm run dev` + `cd frontend && npm run dev` + +## Architecture Rules + +Before submitting a PR, ensure your code follows these rules: + +1. **Domain layer** — No imports from `kysely`, `express`, `playwright` or any infrastructure +2. **Cross-module communication** — Only via EventBus (no direct module imports) +3. **Use cases** — Must return `Result`, never throw business errors +4. **No `any`** — All new code must have explicit TypeScript types + +## Making Changes + +1. Create a feature branch from `main` +2. Write tests for new functionality +3. Run the full verification: `npm run build && cd frontend && npm run build && cd .. && npm run test` +4. Submit a pull request + +## Commit Messages + +Follow the pattern: `feat(module): description` or `fix(module): description` + +## Reporting Issues + +Please use [GitHub Issues](https://github.com/your-org/abe/issues) with: +- Steps to reproduce +- Expected vs actual behavior +- ABE version and Node.js version diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..02afaf4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024-2026 ABE Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE-ENTERPRISE b/LICENSE-ENTERPRISE new file mode 100644 index 0000000..50777d8 --- /dev/null +++ b/LICENSE-ENTERPRISE @@ -0,0 +1,24 @@ +ABE ENTERPRISE LICENSE + +Copyright (c) 2024-2026 ABE Contributors + +ENTERPRISE FEATURES LICENSE + +The enterprise features of ABE (including but not limited to: SSO/SAML/OIDC +integration, LDAP/Active Directory, advanced audit logs, session management +dashboard, white-labeling, and data retention policies) are licensed under a +commercial license. + +To obtain an enterprise license, contact: enterprise@abe.example.com + +PERMITTED USES (with valid enterprise license key): +- Deploy ABE Enterprise in your organization +- Use all enterprise features +- Create internal deployments + +PROHIBITED USES: +- Redistribution of enterprise features +- Sublicensing +- Removing license validation + +The core ABE platform is available under the MIT License (see LICENSE). diff --git a/README.md b/README.md index 261dd72..3f38a85 100644 --- a/README.md +++ b/README.md @@ -1,256 +1,153 @@ # ABE — Autonomous Bug Explorer -[![Build](https://img.shields.io/github/actions/workflow/status/your-org/abe/abe-example.yml?branch=main&label=build)](https://github.com/your-org/abe/actions) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -[![Version](https://img.shields.io/badge/version-0.1.0-blue)](package.json) +> "Playwright discovers what you test. ABE discovers what you miss." -> **"Playwright discovers what you test. ABE discovers what you miss."** +[![Build](https://img.shields.io/github/actions/workflow/status/your-org/abe/ci.yml?branch=main)](https://github.com/your-org/abe/actions) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/) +[![Node.js](https://img.shields.io/badge/Node.js-20-green)](https://nodejs.org/) -An enterprise-grade, self-hosted platform for autonomous bug discovery in web applications. ABE explores your app like a real user, injects invalid inputs (fuzzing), detects anomalies, and generates reproducible bug reports — all without writing a single test. +ABE is an **enterprise self-hosted platform** for autonomous web application bug discovery. It explores apps like a real user, injects invalid inputs (fuzzing), detects anomalies, and generates reproducible bug reports. + +--- ## Features -- **Autonomous exploration** — navigates your app using a seeded, deterministic algorithm -- **Smart fuzzing** — injects empty values, oversized strings, special chars, type mismatches and boundary values into every input -- **Anomaly detection** — catches HTTP errors, JS exceptions, console errors, and accessibility violations -- **Reproducible reports** — every finding includes an exact action trace + generated Playwright test -- **Real-time dashboard** — watch explorations live with severity heatmaps and trend charts -- **CI/CD integration** — JUnit XML output, GitHub Action, exit codes for threshold-based gating -- **Auth support** — cookies, headers, or login flow for authenticated app exploration -- **Enterprise licensing** — RSA-signed license keys, RBAC, API keys, Slack/GitHub/Jira integrations +- **Autonomous Exploration** — BFS-based state graph exploration with deterministic seeds +- **Smart Fuzzing** — 5 strategies: empty, oversized, special characters, type mismatch, boundary values +- **Visual Regression** — pixel-level screenshot comparison with Playwright + pixelmatch +- **Accessibility Auditing** — WCAG violations via axe-core +- **Reproducible Reports** — generates Playwright test scripts, Markdown, JSON, PDF reports +- **Real-time Dashboard** — live WebSocket feed with severity charts and KPI cards +- **Auth & RBAC** — multi-user, organizations, roles (owner/admin/member/viewer), API keys +- **Integrations** — Slack, GitHub Issues, Jira, custom webhooks +- **Scheduling** — cron-based automated explorations +- **CLI + CI/CD** — JUnit XML output, GitHub Actions integration +- **API Documentation** — OpenAPI 3.1 + Scalar UI at `/api-docs` +- **Licensing** — RSA-signed license keys with feature gating (Free/Pro/Enterprise) + +--- ## Quick Start +### Prerequisites + +- Node.js 20+ +- npm 10+ + +### Development + ```bash # Install dependencies npm install +cd frontend && npm install && cd .. -# Install Playwright browser -npx playwright install chromium +# Start development servers +npm run dev # Backend on :3001 +cd frontend && npm run dev # Frontend on :5173 -# Explore your app (inline mode — no server needed) -npm run abe -- explore --url http://localhost:3000 +# Database migrations +npm run db:migrate -# Start the full dashboard (API server + React frontend) -npm run dev:all -# Then open http://localhost:5173 +# Run tests +npm run test + +# Build +npm run build +cd frontend && npm run build ``` -## CLI Reference - -### `abe explore` — Run an exploration +### Docker ```bash -npm run abe -- explore [options] +# Start all services +docker compose up -d --build + +# Production +docker compose -f docker-compose.prod.yml up -d --build ``` -| Option | Default | Description | -|--------|---------|-------------| -| `--url ` | *(required)* | Target URL to explore | -| `--config ` | — | JSON config file (merged with flags) | -| `--seed ` | `42` | Deterministic seed | -| `--max-states ` | `50` | Max states to visit | -| `--max-depth ` | `5` | Max click depth | -| `--allowed-domains ` | *(from URL)* | Comma-separated allowed domains | -| `--excluded-paths

` | — | Comma-separated paths to skip | -| `--auth-type ` | — | `cookies` \| `headers` \| `login_flow` | -| `--output ` | `human` | `human` \| `json` \| `junit` \| `markdown` | -| `--reports-dir

` | `./reports` | Output directory | -| `--fail-on-severity ` | — | Exit 1 if finding at `low`/`medium`/`high`/`critical` or above | -| `--fail-on-anomaly` | — | Exit 1 if any finding found | -| `--server ` | — | Remote ABE server URL (skips inline engine) | -| `--api-key ` | — | API key for remote server | +The app will be available at `http://localhost:5173`. -**Exit codes:** `0` = clean, `1` = findings over threshold, `2` = error +--- -#### Examples +## CLI Usage ```bash -# Basic exploration -npm run abe -- explore --url https://staging.myapp.com - -# CI mode — fail on high/critical findings, output JUnit -npm run abe -- explore \ - --url https://staging.myapp.com \ - --max-states 100 \ - --output junit \ +# Run an exploration +node dist/cli/abe.js explore --url https://example.com \ + --output json \ --fail-on-severity high -# Authenticated exploration (login flow) -npm run abe -- explore \ - --url https://staging.myapp.com \ - --auth-type login_flow \ - --login-url https://staging.myapp.com/login \ - --username ci@example.com \ - --password secret +# Generate a report +node dist/cli/abe.js report --session SESSION_ID -# Load config from JSON file -npm run abe -- explore --url https://staging.myapp.com --config abe.config.json - -# Remote server mode (delegates to ABE server) -npm run abe -- explore \ - --url https://staging.myapp.com \ - --server https://abe.internal.company.com \ - --api-key $ABE_API_KEY +# Check server status +node dist/cli/abe.js status ``` -#### Config File Format (`abe.config.json`) - -```json -{ - "maxStates": 100, - "maxDepth": 8, - "seed": 1337, - "allowedDomains": ["staging.myapp.com"], - "excludedPaths": ["/logout", "/admin"] -} -``` - -### `abe report` — Generate a report - -```bash -npm run abe -- report --session [options] -``` - -| Option | Default | Description | -|--------|---------|-------------| -| `--session ` | *(required)* | Session ID to report on | -| `--server ` | `http://localhost:3001` | ABE server URL | -| `--api-key ` | — | API key | -| `--format ` | `pdf` | `pdf` \| `html` \| `json` | -| `--output ` | `./abe-report-.` | Output file path | - -```bash -npm run abe -- report \ - --session abc123 \ - --server https://abe.internal.company.com \ - --api-key $ABE_API_KEY \ - --format pdf \ - --output ./security-report.pdf -``` - -### `abe status` — Check server health - -```bash -npm run abe -- status [options] -``` - -| Option | Default | Description | -|--------|---------|-------------| -| `--server ` | `http://localhost:3001` | ABE server URL | -| `--api-key ` | — | API key | -| `--json` | — | JSON output | - -```bash -npm run abe -- status --server https://abe.internal.company.com -# ✓ ABE server is ready at https://abe.internal.company.com -# 2 active session(s): -# [abc123] https://staging.myapp.com — 42 states explored -``` - -## CI/CD Integration - -### GitHub Actions — Composite Action +### CI/CD Integration ```yaml -steps: - - uses: actions/checkout@v4 - - - name: Run ABE - uses: ./.github/actions/abe-explore - with: - url: https://staging.myapp.com - max-states: '50' - fail-on-severity: high - output: junit - - - name: Publish results - if: always() - uses: EnricoMi/publish-unit-test-result-action@v2 - with: - files: abe-results.xml +# .github/workflows/abe.yml +- uses: ./.github/actions/abe-explore + with: + url: https://staging.example.com + fail-on-severity: high + api-key: ${{ secrets.ABE_API_KEY }} ``` -### GitHub Actions — Inline - -```yaml -- name: Run ABE - run: | - npm run abe -- explore \ - --url https://staging.myapp.com \ - --max-states 50 \ - --output junit \ - --fail-on-severity high -``` - -### Docker CI Image - -```bash -# Build the CI image (includes Playwright/Chromium) -docker build -f Dockerfile.ci -t abe-ci . - -# Run exploration in Docker -docker run --rm \ - -v $(pwd)/abe-reports:/reports \ - abe-ci explore \ - --url http://host.docker.internal:3000 \ - --output junit \ - --fail-on-severity high -``` - -### JUnit XML Output - -With `--output junit`, ABE writes `abe-results.xml`: -- Each **state visited** = a passing test case -- Each **finding** = a failing test case with severity and description - -Integrates with GitHub Actions, Jenkins, GitLab CI, CircleCI, and any JUnit-compatible reporter. - -## Web Dashboard - -```bash -# Start both backend (port 3001) and frontend (port 5173) -npm run dev:all -``` - -Open `http://localhost:5173`. First run prompts you to create an admin account and organization. - -## Docker - -```bash -docker compose up --build -``` - -| Service | Port | Description | -|---------|------|-------------| -| Backend | 3001 | Express API + socket.io | -| Frontend | 5173 | React dashboard (nginx) | +--- ## Architecture +ABE uses a **modular monolith hexagonal architecture** with bounded contexts: + ``` -Domain (pure TypeScript — no infrastructure dependencies) - ↑ -Application (use cases, commands, queries, event handlers) - ↑ -Infrastructure (Kysely/SQLite, Playwright, Express controllers) +src/ +├── shared/ → Domain building blocks (Entity, ValueObject, Result, EventBus) +├── modules/ +│ ├── crawling/ → Session management + Playwright crawler +│ ├── fuzzing/ → Input fuzzing strategies +│ ├── findings/ → Bug report lifecycle +│ ├── auth/ → Users, organizations, RBAC +│ ├── reporting/ → PDF/HTML/JSON report generation +│ ├── integrations/→ Slack, GitHub, Jira, webhooks +│ ├── scheduling/ → Cron-based automation +│ ├── licensing/ → RSA license validation +│ └── visual-regression/ → Screenshot comparison +├── api/ → Express server + OpenAPI docs +├── realtime/ → Socket.io gateway +├── jobs/ → SQLite-backed job queue +└── cli/ → Commander CLI ``` -**Modules:** `crawling` · `findings` · `fuzzing` · `auth` · `reporting` · `integrations` · `licensing` +**Architectural rules:** +1. Domain never imports infrastructure +2. Cross-module communication only via EventBus +3. Use cases return `Result`, never throw +4. Controllers are thin — delegate to use cases -Cross-module communication via `EventBus` only — bounded contexts never import each other directly. +--- -## Development +## API Documentation -```bash -npm run build # Compile TypeScript -npm run test # Run tests (Vitest) -npm run lint # ESLint -npm run db:migrate # Apply database migrations -cd frontend && npm run build # Build frontend -docker compose up -d --build # Full stack with Docker -``` +Once running, visit `http://localhost:3001/api-docs` for the interactive Scalar API reference. + +Endpoints: +- `POST /api/auth/register` — Register +- `POST /api/auth/login` — Login +- `GET /api/sessions` — List explorations +- `POST /api/sessions` — Start exploration +- `GET /api/findings` — List findings +- `POST /api/reports` — Generate report +- `GET /api/schedules` — List schedules +- `GET /api/visual/comparisons` — Visual regression review + +--- ## License -Core: [MIT](LICENSE) · Enterprise features require a valid license key. +ABE core is open-source under the [MIT License](LICENSE). + +Enterprise features (SSO, LDAP, advanced audit logs) require a commercial license. See [LICENSE-ENTERPRISE](LICENSE-ENTERPRISE). diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4ac6f7a..1312b86 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -25,6 +25,7 @@ import { LicenseSection } from '@/pages/settings/LicenseSection' import { SchedulesSection } from '@/pages/settings/SchedulesSection' import { Reports } from '@/pages/Reports' import { VisualReview } from '@/pages/VisualReview' +import { ErrorBoundary } from '@/components/layout/ErrorBoundary' export default function App() { return ( @@ -42,13 +43,13 @@ export default function App() { } > - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> }> } /> } /> diff --git a/frontend/src/components/layout/ErrorBoundary.tsx b/frontend/src/components/layout/ErrorBoundary.tsx new file mode 100644 index 0000000..a5a815c --- /dev/null +++ b/frontend/src/components/layout/ErrorBoundary.tsx @@ -0,0 +1,54 @@ +import { Component, type ReactNode } from 'react' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' + +interface Props { + children: ReactNode + fallback?: ReactNode +} + +interface State { + hasError: boolean + error: Error | null +} + +export class ErrorBoundary extends Component { + constructor(props: Props) { + super(props) + this.state = { hasError: false, error: null } + } + + static getDerivedStateFromError(error: Error): State { + return { hasError: true, error } + } + + handleReset = () => { + this.setState({ hasError: false, error: null }) + } + + render() { + if (this.state.hasError) { + if (this.props.fallback) return this.props.fallback + + return ( +
+ + + Something went wrong + + +

+ {this.state.error?.message ?? 'An unexpected error occurred.'} +

+ +
+
+
+ ) + } + + return this.props.children + } +}