AppSecOne Security Posture & Release Readiness Intelligence
Enterprise-grade security posture dashboard that answers: "Are we ready to release?"
Fortify SSC · Policy Engine · Trend Analytics · Waiver Management · Release Readiness · i18n
What is AppSecOne? • Screenshots • Features • Installation • Quick Start • Configuration • API • Architecture • Testing
AppSecOne is an enterprise application security dashboard that aggregates vulnerability data from Fortify SSC, evaluates release readiness through a deterministic policy engine, and presents the results in a real-time web dashboard with full API access.
Security teams, engineering managers, and release coordinators use it to answer critical questions every sprint:
- "Can we ship this release?" — Policy engine evaluates every repository against configurable thresholds for critical, high, and medium findings.
- "What's our security posture across the portfolio?" — Aggregated severity breakdowns, readiness scores, and trend analytics across all repositories.
- "Are we improving or regressing?" — Historical trend snapshots with 5% threshold direction classification (improving / stable / worsening).
- "Which teams need attention?" — Filter by team, repository type, criticality level, and readiness status.
- "What did we waive, and why?" — Full waiver management with audit trail, expiry tracking, and approval workflows.
AppSecOne syncs from Fortify SSC on a configurable interval (default 30 minutes), stores a local read model in SQLite, and serves a server-rendered dashboard via FastAPI + Jinja2 — no JavaScript frameworks, no build steps, no external CSS dependencies.
Aggregated severity breakdown, readiness donut chart, repository cards with status indicators, trend badges, and policy evaluation — all in a single overview.
Deep-dive into a single repository — findings table with severity badges, policy evaluation with blocker panels, stacked severity bar, and historical trend chart.
- Fortify SSC sync — periodic (30 min) and on-demand, with auto-pagination and retry
- Full re-sync every 24 hours to catch deletions and metadata changes
- Concurrent sync — up to 5 parallel project syncs for large portfolios
- Local SQLite read model — fast queries, offline resilience, zero external DB dependencies
- Deterministic evaluation — pure functions, no side effects, fully testable
- Configurable thresholds — max critical, max high, max medium per severity
- Finding age limits — flag findings older than N days (e.g., 30 days for critical)
- Named policy profiles — different rules for different criticality levels (e.g.,
strictfor business-critical) - Waiver management — create, revoke, and track waivers with expiry and audit trail
- Risk acceptance — configurable per policy profile
- Server-side rendered — Jinja2 templates with vanilla JS, no framework, no build step
- Responsive layout — works on desktop, tablet, and mobile
- 4 themes — light, dark, midnight, and system auto-detect
- Custom design system — CSS custom properties for tokens, severities, and branding
- Internationalization — full i18n with locale catalogs (English, Polish)
- Accessibility — ARIA roles, focus-visible indicators, keyboard navigation, screen reader support
- Export — findings to CSV/JSON, dashboard to standalone HTML
- Trend snapshots — daily aggregates for historical analysis
- Direction classification — improving / stable / worsening with 5% threshold
- Configurable period — 7 to 365 days of trend data
- Health probes — Kubernetes-ready
/health/liveand/health/ready - SSE streaming — real-time sync progress via Server-Sent Events
- Correlation IDs — end-to-end request tracing
- Structured logging — JSON-ready, verbose mode via CLI flag
- What is AppSecOne?
- Screenshots
- Features
- Installation
- Quick Start
- Configuration
- Web Server Mode
- CLI Reference
- API Reference
- Architecture
- Testing
- Security
- Contributing
- Changelog
- Author & License
- Python 3.12+
- Access to a Fortify SSC instance (with a valid API token)
git clone https://github.com/polprog-tech/AppSecOne.git
cd AppSecOne
python3 -m venv .venv
source .venv/bin/activate
pip install -e .pip install -e ".[dev]"1. Create your configuration file:
cp examples/config.json appsecone.json
cp examples/.env.example .env2. Set your Fortify SSC token:
export FORTIFY_SSC_TOKEN=your-fortify-token-here3. Validate the configuration:
appsecone validate --config appsecone.json4. Run your first sync:
appsecone sync --config appsecone.json5. Launch the dashboard:
appsecone serve --config appsecone.jsonOpen http://127.0.0.1:8090 in your browser.
{
"fortify": {
"base_url": "https://fortify.corp.example.com/ssc",
"token_env_var": "APPSECONE_FORTIFY_TOKEN",
"timeout_seconds": 30,
"max_retries": 3,
"verify_ssl": true
},
"sync": {
"interval_minutes": 30,
"full_sync_interval_hours": 24,
"max_concurrent": 5,
"enabled": true
},
"policy": {
"max_critical": 0,
"max_high": 5,
"max_open_findings": null,
"max_age_days_critical": 30,
"max_age_days_high": 90,
"allow_risk_acceptance": true
},
"branding": {
"title": "AppSecOne",
"company": "Your Company",
"primary_color": "#fb6400"
},
"settings": {
"theme": "system",
"log_level": "info",
"host": "127.0.0.1",
"port": 8090
},
"repositories": [
{
"slug": "web-app",
"name": "WebApp",
"fortify_project_name": "Corp WebApp",
"type": "web",
"team": "platform",
"criticality": "high",
"layer": "frontend"
}
],
"policy_profiles": {
"strict": {
"max_critical": 0,
"max_high": 0,
"max_medium": 10,
"max_age_days_critical": 14,
"max_age_days_high": 30
}
}
}| Variable | Description | Default |
|---|---|---|
APPSECONE_FORTIFY_TOKEN |
Fortify SSC API token (required) | — |
APPSECONE_HOST |
Server bind address | 127.0.0.1 |
APPSECONE_PORT |
Server bind port | 8090 |
APPSECONE_DEBUG |
Enable debug mode | false |
APPSECONE_LOG_LEVEL |
Log level (debug, info, warning, error) |
info |
APPSECONE_API_KEY |
API key for admin endpoints (optional) | — |
APPSECONE_ALLOW_FRAMING |
Allow embedding in iframes | false |
APPSECONE_CORS_ORIGINS |
Allowed CORS origins (comma-separated) | — |
HTTP_PROXY |
Corporate HTTP proxy | — |
HTTPS_PROXY |
Corporate HTTPS proxy | — |
SSL_CERT_FILE |
Custom CA certificate bundle | — |
Start the web server with custom host and port:
appsecone serve --config appsecone.json --host 0.0.0.0 --port 8080 --verboseThe server provides:
- Server-side rendered dashboard at
/(Jinja2 + HTMX, no JS build step) - RESTful JSON API under
/api/for programmatic access - Health probes at
/health/liveand/health/readyfor container orchestration - SSE streaming for real-time sync progress at
/api/admin/sync/stream
Tip: Use
--verboseto enable detailed request logging with correlation IDs for debugging.
AppSecOne ships a Typer-based CLI with five commands:
| Command | Description | Key Options |
|---|---|---|
appsecone serve |
Start the web dashboard server | --config, --host, --port, --verbose |
appsecone sync |
Run a one-time sync from Fortify SSC | --config, --verbose |
appsecone evaluate |
Evaluate release readiness (all or one repo) | --config, --repo, --verbose |
appsecone validate |
Validate a configuration file | --config |
appsecone version |
Show AppSecOne version | — |
# Evaluate a single repository
appsecone evaluate --config appsecone.json --repo web-app
# Evaluate all repositories
appsecone evaluate --config appsecone.json --verbose
# Validate config before deployment
appsecone validate --config /etc/appsecone/production.jsonAll JSON endpoints return {"ok": true, ...} on success and {"ok": false, "error": "..."} on failure.
| Method | Endpoint | Description |
|---|---|---|
GET |
/health/live |
Liveness probe — always returns 200 |
GET |
/health/ready |
Readiness probe — checks config, DB, sync freshness |
GET |
/api/status |
System status: version, uptime, config state, last sync |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/overview |
Portfolio-wide severity breakdown and readiness summary |
GET |
/api/trends |
Portfolio trend data over a configurable period (?period=30) |
GET |
/api/trends/{slug} |
Trend data for a single repository |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/repositories |
Paginated list with filters: type, team, readiness, search, sort_by, sort_dir |
GET |
/api/repositories/{slug} |
Detail view for a single repository |
GET |
/api/repositories/{slug}/findings |
Findings for a repository with severity, status, category filters |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/findings |
All findings, paginated, with filters: severity, status, repository, category, search |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/readiness |
Readiness summary for all repositories |
GET |
/api/readiness/{slug} |
Detailed readiness evaluation for one repository |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/policies |
Default policy and all named policy profiles |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/waivers |
List all active waivers |
POST |
/api/waivers |
Create a waiver (requires repository_id, severity, reason, approved_by) |
DELETE |
/api/waivers/{waiver_id} |
Revoke a waiver |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/sync-runs |
Sync run history (?limit=20) |
POST |
/api/admin/sync |
Trigger manual sync (background task) |
POST |
/api/admin/sync/stream |
Trigger sync with SSE real-time progress stream |
PUT |
/api/admin/sync-interval |
Update sync interval (body: {"interval_minutes": 30}) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/setup/test-connection |
Test connectivity to Fortify SSC |
POST |
/api/setup/discover |
Discover projects and versions from Fortify SSC |
POST |
/api/setup/save-config |
Save wizard configuration to appsecone.json |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/export/findings |
Export findings as CSV or JSON (?format=csv) |
GET |
/api/export/dashboard |
Export current dashboard as standalone HTML |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/i18n/{locale} |
Translation catalog for the given locale |
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Portfolio dashboard |
GET |
/repo/{slug} |
Repository detail page |
GET |
/findings |
Findings explorer with filters |
GET |
/policies |
Policy & waiver management page |
GET |
/admin |
Admin / system status page |
GET |
/setup |
Integration setup wizard |
Note: State-changing endpoints (
POST,PUT,DELETE,PATCH) require a valid API key whenAPPSECONE_API_KEYis set. See Security.
AppSecOne follows a clean layered architecture — the domain layer has zero I/O dependencies, the application layer orchestrates async workflows, and the web layer is a thin adapter.
src/appsecone/
├── __init__.py # Version (0.1.0)
├── domain/ # Pure domain: enums, models, policy engine
├── config/ # JSON config loading + schema validation
├── fortify/ # Fortify SSC integration layer
│ ├── client/ # HTTP client, auth, endpoints
│ └── mappers/ # DTO → domain mapping
├── persistence/ # SQLite DAL (repos, findings, aggregates)
├── application/ # Service layer (sync, query)
├── analysis/ # Trend analytics & historical snapshots
├── presentation/ # Jinja2 renderer, view models, theme, templates
├── web/ # FastAPI server + middleware stack
├── cli/ # Typer CLI commands
├── i18n/ # Internationalization catalogs
└── shared/ # Logging, type aliases
- Domain purity — business rules live in
domain/with zero imports from infrastructure layers. - Dependency inversion — application services depend on abstractions, not concrete implementations.
- Local read model — Fortify SSC is the source of truth, but all reads come from a local SQLite store for speed and resilience.
- Server-rendered first — Jinja2 templates with HTMX for interactivity; no SPA, no JS bundler.
- Configuration as code — all behavior is driven by a single JSON config file + environment variables.
CLI / Web → Application → Domain
↓ ↑
Persistence Analysis
↓
Fortify (sync only)
AppSecOne has 464 tests covering domain logic, policy evaluation, API endpoints, persistence, Fortify integration, UX/UI regression, accessibility, and localization.
# All tests
python3 -m pytest
# With coverage report
python3 -m pytest --cov=appsecone --cov-report=term-missing
# Unit tests only
python3 -m pytest tests/unit/| Directory | Scope | What it covers |
|---|---|---|
tests/unit/domain/ |
Unit | Enums, models, value objects |
tests/unit/policy/ |
Unit | Policy engine rule evaluation |
tests/unit/mappers/ |
Unit | DTO → domain mapping (Fortify → domain) |
tests/unit/presentation/ |
Unit | Template rendering, view models, UX/UI regression |
tests/unit/shared/ |
Unit | i18n, networking, utilities |
tests/unit/test_audit_regression.py |
Regression | UX audit fixes — layout, a11y, responsive |
tests/unit/test_blocker_regression.py |
Regression | Production blocker fixes — security, data integrity |
Note: Async tests run automatically via
pytest-asynciowithasyncio_mode = "auto". HTTP mocking usesrespx.
AppSecOne ships with a six-layer middleware stack applied to every request:
| # | Middleware | Purpose |
|---|---|---|
| 1 | SecurityHeadersMiddleware | Injects X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, CSP, XSS protection |
| 2 | RequestLoggingMiddleware | Generates correlation IDs, logs method/path/status/duration for audit trails |
| 3 | RateLimitMiddleware | Per-IP fixed-window: 120 req/min general, 5 req/min for /api/admin/* POST |
| 4 | CORSMiddleware | Configurable origins via APPSECONE_CORS_ORIGINS, restricts methods and headers |
| 5 | CSRFMiddleware | Origin/Referer validation on all state-changing requests (POST/PUT/DELETE/PATCH) |
| 6 | APIKeyMiddleware | Optional HMAC-verified API key for admin endpoints via X-API-Key header |
- All state-changing methods require valid
OriginorReferermatching theHost - Requests with
X-API-Keybypass CSRF (machine-to-machine) - Requests without cookies bypass CSRF (no session to exploit)
- API-style requests (
Content-Type: application/jsonorX-Requested-With: XMLHttpRequest) are allowed - Health and streaming endpoints are exempt
- Enabled when
APPSECONE_API_KEYenvironment variable is set - Uses
hmac.compare_digest()for timing-safe comparison (prevents timing attacks) - Protects all state-changing methods; GET requests are exempt
- Returns
401 Unauthorizedon invalid or missing key
- No hardcoded credentials — all secrets loaded from environment variables
- Fortify token referenced by env var name in config (
token_env_var), never stored in JSON - API key read from
APPSECONE_API_KEYat startup - Token expiry validation for Fortify SSC authentication
AppSecOne includes a centralized SSL context factory (shared/network.py) that
automatically detects corporate CA certificates. Resolution order:
SSL_CERT_FILE/REQUESTS_CA_BUNDLEenv var (explicit override)certifipackage (ships Mozilla CA bundle)- macOS system keychain (automatic — includes corporate CAs like Zscaler)
- Python default
macOS / Linux — export your corporate CA bundle:
# macOS: extract certs from system keychain
security find-certificate -a -p \
/Library/Keychains/System.keychain \
/System/Library/Keychains/SystemRootCertificates.keychain \
> ~/combined-ca-bundle.pem
# Add to shell profile (~/.zshrc or ~/.bashrc)
export SSL_CERT_FILE=~/combined-ca-bundle.pem
export REQUESTS_CA_BUNDLE=~/combined-ca-bundle.pemProxy configuration:
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1,.internal.example.comFor detailed troubleshooting, see docs/troubleshooting.md.
Contributions are welcome! Please read our Contributing Guide before submitting a pull request.
Created and maintained by POLPROG (@POLPROG).
AGPL-3.0 — see LICENSE

