Skip to content

E-Gregorio/qasl-test-security

Repository files navigation

QASL Test Security

Forensic-grade Application Security testing framework with multi-layer SAST/DAST/SCA coverage and the CFQI scoring algorithm.

License: MIT QASL Ecosystem CFQI Algorithm OWASP Top 10 ASVS NIST Docker Python Node

Author: Elyer Maldonado — AI Quality & Risk Architect Sibling project: INGRID — AI Security Testing Framework


TL;DR. Boot 3 Docker containers (~700 MB RAM), seed a synthetic banking-like portfolio, open the live Grafana dashboards on localhost:3003, hit the FastAPI normalizer on localhost:8001/docs, and emit two A4 forensic PDFs (one corporate-style for CISOs, one dictamen pericial for auditors). The whole pipeline runs on a laptop and is built on the proprietary CFQI algorithm (Code Forensic Quality Index, sibling of AFQI from INGRID).


📸 Visual preview

Executive PDF Report
3 pages · INGRID corporate style
Dictamen Forense CFQI
1 page · AFQI forensic style
Executive PDF cover Dictamen Forense CFQI
Grafana — Executive Overview
Severity KPIs, donut, CFQI per project, 30-day trend
Grafana — CFQI Forensic Analysis
4 gauges (overall + D1/D2/D3) + per-project audit table
Grafana Executive Overview Grafana CFQI Forensic
Grafana — OWASP Top 10 & Compliance
Coverage by category, top criticals, MTTR SLA
Grafana — Folder view
Three dashboards under "QASL Test Security"
OWASP & Compliance dashboard QASL Test Security folder
FastAPI Swagger UI
Normalizer service — interactive API docs
OpenAPI JSON
Machine-readable contract for client generation
FastAPI Swagger UI OpenAPI JSON

📑 Table of contents

  1. The QASL Ecosystem
  2. What makes this different
  3. Architecture
  4. Pre-requisites
  5. Installation — commands in order
  6. First-run verification
  7. Daily operations
  8. The CFQI algorithm
  9. Standards & compliance
  10. Repository structure
  11. Troubleshooting
  12. Roadmap
  13. Author, license & disclaimer

🧬 1. The QASL Ecosystem

This module is part of the QASL (Quality Assurance Security Lab) Ecosystem:

QASL Ecosystem
├── MATHCORE             ← mathematical & statistical engine
├── INGRID               ← AI/LLM Security Testing (AFQI algorithm)
└── QASL Test Security   ← Application Security Testing (CFQI algorithm) ← you are here

INGRID audits AI systems forensically using the AFQI algorithm. QASL Test Security does the same for traditional applications using the CFQI algorithm — the natural sibling, applied to code.

Module Domain Algorithm Status
MATHCORE Mathematical / statistical engine Shared core
INGRID AI / LLM Security Testing AFQI v1.0 Production
QASL Test Securityhere Application Security Testing CFQI v1.0 Production

🎯 2. What makes this different

Most security frameworks aggregate findings from scanners. QASL Test Security goes further with three differentiators:

2.1 CFQI Algorithm — Code Forensic Quality Index

A proprietary scoring algorithm modeled after AFQI that produces a single 0-100 score with grade A/B/C/D/F, computed from three forensic dimensions:

CFQI = 100 · [α·S(c) + β·D(d) + γ·R(r)] − Λ(C) · μ(R)

  S(c) = Static layer       (SAST + Secrets + IaC)         α = 0.35
  D(d) = Dependencies layer (SCA + SBOM + Container)       β = 0.30
  R(r) = Runtime layer      (DAST + API + Fuzzing)         γ = 0.35
  Λ(C) = Compliance penalty (uncovered OWASP Top 10 categories)
  μ(R) = VCR risk multiplier (Value · Cost · Risk profile)

See docs/cfqi-algorithm.md for the full mathematical justification.

2.2 Two separate forensic PDFs (by audience)

The framework deliberately produces two PDFs, not one — because mixing visual languages confuses readers:

PDF Pages Style Audience
QASL-Executive-Report 3 INGRID corporate (dark blue + gold) CISOs, management, stakeholders
QASL-CFQI-Dictamen-Forense 1 AFQI pericial (beige + serif) Auditors, peritos, legal, compliance

2.3 Universal stack coverage

One framework, all the major automatable security layers:

Layer Tool Standard
SAST Semgrep CWE, OWASP Top 10
SCA Trivy NVD CVE, CycloneDX SBOM
Secrets gitleaks Pattern matching
IaC Checkov / Trivy CIS Benchmarks
Container Trivy NVD CVE
DAST OWASP ZAP OWASP Top 10, CWE
API OWASP ZAP / Nuclei OWASP API Security

🏛️ 3. Architecture

Four independent layers, decoupled by a unified schema:

┌── 1) Security Tools (CI/CD or local) ─────────────────┐
│  Semgrep · Trivy · OWASP-ZAP · gitleaks · Checkov   │
└──────────────────────────┬───────────────────────────┘
                           │ SARIF / JSON
                           ▼
              ┌── 2) Normalizer (FastAPI) ──┐
              │  Parse · Dedup · Enrich     │
              └────────────┬────────────────┘
                           │
                           ▼
              ┌── 3) PostgreSQL (single source of truth) ──┐
              │  projects · scans · findings · cfqi_scores │
              │  views: v_active_findings · v_latest_cfqi  │
              └──┬──────────────────────────────────┬──────┘
                 │                                  │
                 ▼                                  ▼
       ┌── 4a) Grafana ──┐               ┌── 4b) PDF Generator ──┐
       │  3 dashboards   │               │  CFQI calculator      │
       │  Live queries   │               │  Executive Report     │
       │  Time series    │               │  Dictamen Forense     │
       └─────────────────┘               └───────────────────────┘
        Live observability               Forensic point-in-time
Component Stack RAM (idle / load) Host port Container port
PostgreSQL 16-alpine SQL + JSONB 80 MB / 200 MB 5433 5432
Normalizer FastAPI + uvicorn 60 MB / 150 MB 8001 8000
Grafana 10.4.2 Observability platform 150 MB / 250 MB 3003 3000
PDF Generator Node + PDFKit — / 150 MB transient

ℹ️ Why non-standard host ports? This module is designed to coexist with sibling QASL stacks (e.g. QASL Framework) on the same host. To avoid collisions, host ports are non-standard (3003 · 8001 · 5433), container names are prefixed qasl-test-* and Docker volumes are prefixed qasl_test_*. Internally, containers still use canonical ports (5432/8000/3000).

Detailed architecture: docs/architecture.md


✅ 4. Pre-requisites

Requirement Minimum version Verify with
OS Windows 11 / macOS 12+ / Linux (any modern)
Docker Desktop 29.x docker --version
Docker Compose v2 v2.x docker compose version
Node.js 18+ node --version
Python 3.10+ python --version
Free RAM ~2 GB Task Manager / free -h
Free disk ~3 GB (first image pull)
Free host ports 3003, 8001, 5433 Get-NetTCPConnection -LocalPort 3003,8001,5433 (Windows)
lsof -i :3003,:8001,:5433 (macOS/Linux)

🚀 5. Installation — commands in order

Run in this exact order from the project root. Windows users: PowerShell. macOS/Linux: bash.

5.1 — Clone the repository

git clone https://github.com/E-Gregorio/qasl-test-security.git
cd qasl-test-security

5.2 — Create the environment file

# Windows (PowerShell)
Copy-Item .env.example .env
# macOS / Linux
cp .env.example .env

Default credentials in .env.example are for development only (qasl_admin / changeme_dev_only). Rotate before any non-local deployment.

5.3 — Boot the Docker stack

docker compose up -d

This will:

  1. Pull postgres:16-alpine, grafana/grafana:10.4.2 (~200 MB total, first time only)
  2. Build the local normalizer image (Python 3.12-slim, ~150 MB)
  3. Start all 3 services with health checks
  4. Provision Grafana datasource + 3 dashboards automatically

Wait 60–90 seconds the first time, then verify:

docker compose ps

Expected: 3 containers qasl-test-postgres, qasl-test-normalizer, qasl-test-grafana — all running, postgres + normalizer healthy.

5.4 — Install the seed dependency

# Windows
pip install psycopg2-binary
# macOS / Linux
pip3 install psycopg2-binary

5.5 — Seed synthetic demo data (4 projects, ~96 findings)

# Windows — PYTHONIOENCODING required because Python 3.13 stdout uses cp1252
$env:PYTHONIOENCODING = "utf-8"
python seed\seed_demo.py
# macOS / Linux
python3 seed/seed_demo.py

Expected output (last lines):

✅ Seed completed: 4 projects, 96 findings
   📊 Open Grafana at http://localhost:3003

5.6 — Install the PDF generator dependencies

cd pdf-generator
npm install
cd ..

5.7 — Generate the two forensic PDFs (real data from Postgres)

# Windows
$env:DATABASE_URL = "postgresql://qasl_admin:changeme_dev_only@localhost:5433/qasl_security"
cd pdf-generator
node generate-cfqi-report.mjs --project=PaymentService-API
cd ..
# macOS / Linux
export DATABASE_URL="postgresql://qasl_admin:changeme_dev_only@localhost:5433/qasl_security"
cd pdf-generator
node generate-cfqi-report.mjs --project=PaymentService-API
cd ..

Output in pdf-generator/reports/:

  • QASL-Executive-Report-PaymentService-API-{date}.pdf (3 pages)
  • QASL-CFQI-Dictamen-Forense-PaymentService-API-{date}.pdf (1 page)

5.8 — Open the dashboards

URL Credentials
http://localhost:3003 admin / qasl_admin
http://localhost:8001/docs — (open)
http://localhost:8001/redoc — (open)

🆚 One-command alternative (Windows)

Skip steps 5.3–5.8 with the bundled launcher:

.\launch-demo.ps1 -Pdf

Performs all the boot/seed/verify/PDF/browser steps in one shot. See launch-demo.ps1.


🔍 6. First-run verification

Check URL / command Expected
Containers healthy docker compose ps 3/3 running, postgres + normalizer healthy
Normalizer health curl http://localhost:8001/health {"status":"healthy","service":"qasl-normalizer","version":"0.1.0"}
Swagger UI http://localhost:8001/docs 6 endpoints under 3 tags (meta · ingestion · query)
ReDoc http://localhost:8001/redoc Clean markdown-rendered API docs
OpenAPI JSON http://localhost:8001/openapi.json Valid OpenAPI 3.1 with 2 servers, 3 tags, MIT license
Grafana login http://localhost:3003 Login form, admin / qasl_admin works
Dashboards loaded Grafana → Dashboards Folder QASL Test Security with 3 items
API: list projects curl http://localhost:8001/projects 4 projects with their CFQI score
API: project CFQI curl http://localhost:8001/cfqi/PaymentService-API Full breakdown (D1/D2/D3, weights, λ, μ, grade)
PDFs generated pdf-generator/reports/ 2 A4 PDFs, valid %PDF-1.3 magic

🛠️ 7. Daily operations

7.1 Grafana dashboards

Three dashboards auto-provisioned at boot. Each targets a different reader profile.

Grafana folder view

Dashboard URL Audience
Executive Overview http://localhost:3003/d/qasl-executive-overview CISO, management
CFQI Forensic Analysis http://localhost:3003/d/qasl-cfqi-forensic Security engineer
OWASP Top 10 & Compliance http://localhost:3003/d/qasl-owasp-compliance Auditor, compliance

Executive Overview

Executive Overview dashboard

4 severity KPIs, donut chart by scan type, CFQI table per project, 30-day vulnerability trend.

CFQI Forensic Analysis

CFQI Forensic Analysis dashboard

4 gauges — overall CFQI plus per-dimension scores (D1 Static / D2 Dependencies / D3 Runtime) — and a forensic audit table per project.

OWASP Top 10 & Compliance

OWASP & Compliance dashboard

OWASP category distribution, top critical/high findings, MTTR per severity for SLA tracking.

Customizing dashboards: edit the JSON files under grafana/dashboards/ and restart Grafana. UI edits don't persist — provisioning overrides them at restart.

7.2 FastAPI Normalizer

The Normalizer ingests raw scanner output, normalizes findings into a unified schema, deduplicates by SHA256 fingerprint and persists into PostgreSQL.

Swagger UI

Method Path Tag Purpose
GET /health meta Liveness + DB connectivity check
GET / meta Service identity + endpoint catalog
POST /ingest ingestion Submit scanner output (SARIF/JSON)
GET /findings/{project_name} query Active findings, severity-sorted
GET /cfqi/{project_name} query Latest CFQI breakdown
GET /projects query Portfolio view with CFQI per project

Three documentation endpoints:

OpenAPI JSON

Upload a scan via the helper script (bash / WSL)

./scripts/upload_to_normalizer.sh PaymentService-API semgrep SAST semgrep-output.sarif

Or upload via curl

curl -X POST http://localhost:8001/ingest \
     -H "Content-Type: application/json" \
     -d @payload.json

7.3 Forensic PDF reports

Two PDFs are generated per run, intentionally separated.

Executive Report Dictamen Forense
QASL-Executive-Report (3 pp)
INGRID corporate · CISO/management
QASL-CFQI-Dictamen-Forense (1 p)
AFQI pericial · auditor/compliance

Generate (demo mode — synthetic findings)

cd pdf-generator
node generate-cfqi-report.mjs --demo

Generate (real data from Postgres)

# Windows
$env:DATABASE_URL = "postgresql://qasl_admin:changeme_dev_only@localhost:5433/qasl_security"
node generate-cfqi-report.mjs --project=PaymentService-API
# macOS / Linux
DATABASE_URL="postgresql://qasl_admin:changeme_dev_only@localhost:5433/qasl_security" \
  node generate-cfqi-report.mjs --project=PaymentService-API

Both PDFs are deterministic: same input findings → same score, same narrative, same grade. That reproducibility is what makes CFQI defendable to a regulator.


📐 8. The CFQI algorithm

CFQI = 100 · [α·S(c) + β·D(d) + γ·R(r)] − Λ(C) · μ(R)

Three weighted dimensions, minus a compliance penalty, divided by a risk multiplier.

Symbol Meaning Default
S(c) D1 Static layer (SAST + Secrets + IaC) weight α = 0.35
D(d) D2 Dependencies layer (SCA + Container) weight β = 0.30
R(r) D3 Runtime layer (DAST + API + Fuzzing) weight γ = 0.35
Λ(C) Compliance penalty (uncovered OWASP categories) 0–10 pts
μ(R) VCR risk multiplier (Value · Cost · Risk) 1.0 std · 1.5 high · 2.0 critical

Severity weights (points removed per finding)

Severity Penalty
critical 25
high 10
medium 3
low 1
info 0

Grade mapping

Score Grade Verdict Action
≥ 90 A SECURE Maintain controls. Quarterly review.
75–89 B OPERABLE Plan to remediate highs next sprint.
60–74 C CON RESERVAS Re-evaluate weakest dimension.
40–59 D RIESGO ALTO Suspend deployment. Threat-model.
< 40 F INACEPTABLE Do not deploy. External audit.

Reference implementation: pdf-generator/lib/cfqi-calculator.mjs (~150 lines, intentionally readable). Mathematical justification: docs/cfqi-algorithm.md. Project-wide VCR/μ policy (v2.0 guide): docs/vcr-project-profile.md.


📚 9. Standards & compliance

The framework does not invent its own categories — every finding is mapped to industry-recognized standards so any auditor or regulator can cross-validate.

Standard Coverage
OWASP Top 10 2021 Per-finding mapping (owasp_top10 field) + dashboard 03
OWASP ASVS 4.0 Optional owasp_asvs field; matrix in docs/owasp-asvs-mapping.md
OWASP Testing Guide v4.2 Manual checklist in docs/manual-testing-checklist.md
NIST SP 800-218 SSDF Practices referenced in program-level docs
ISO/IEC 27001:2022 Compatible with controls A.5–A.18 on security testing
SARIF v2.1.0 Native parser (Semgrep · Snyk · SonarQube)
CycloneDX SBOM Compatible with Trivy SCA output
CWE / CVE / CVSS / EPSS Cross-referenced per finding when applicable

📁 10. Repository structure

qasl-test-security/
├── README.md                           # ← this file
├── MANUAL.html                         # Operations & user manual (HTML, print-ready)
├── QUICKSTART.md                       # 6-step rapid start
├── INTERVIEW-NOTES.md                  # Talking points for technical interviews
├── docker-compose.yml                  # Stack orchestration (3 services)
├── launch-demo.ps1                     # One-command launcher (Windows)
├── .env.example                        # Environment template
├── img/                                # Screenshots used in this README
├── docs/
│   ├── architecture.md                 # Detailed architecture + threat model
│   ├── architecture-diagram.svg        # Visual diagram
│   ├── cfqi-algorithm.md               # CFQI math & justification
│   ├── threat-modeling.md              # STRIDE template
│   ├── manual-testing-checklist.md     # OWASP Testing Guide checklist
│   ├── owasp-asvs-mapping.md           # ASVS coverage matrix
│   ├── security-program.md             # Program-level context
│   └── vcr-project-profile.md          # VCR project profile → μ policy (v2.0 guide)
├── grafana/
│   ├── provisioning/                   # Auto-provisioned datasource + dashboard providers
│   └── dashboards/
│       ├── 01-executive-overview.json
│       ├── 02-cfqi-forensic-analysis.json
│       └── 03-owasp-top10-coverage.json
├── normalizer/
│   ├── Dockerfile
│   ├── requirements.txt
│   ├── main.py                         # FastAPI app
│   └── parsers/                        # Tool-specific parsers
│       ├── sarif.py                    # Semgrep · Snyk · SonarQube
│       ├── trivy.py                    # SCA · Container · IaC
│       ├── zap.py                      # OWASP ZAP DAST
│       └── gitleaks.py                 # Secret scanners
├── pdf-generator/
│   ├── package.json
│   ├── generate-cfqi-report.mjs        # Produces both PDFs (Executive + Dictamen)
│   ├── lib/
│   │   └── cfqi-calculator.mjs         # CFQI algorithm reference impl
│   └── reports/                        # Generated PDFs land here
├── scripts/
│   └── upload_to_normalizer.sh         # Helper to ingest scanner output
├── seed/
│   └── seed_demo.py                    # Synthetic demo data generator
└── sql/
    └── schema.sql                      # PostgreSQL schema + views

🔧 11. Troubleshooting

Stack won't start: "port already in use"

Another service is bound to 5433, 8001, or 3003. Identify it:

# Windows
Get-NetTCPConnection -LocalPort 3003,8001,5433 | Format-Table
# macOS / Linux
lsof -i :3003 -i :8001 -i :5433

Free the port or remap in docker-compose.yml (e.g. "3004:3000").

Grafana shows "No data" on every panel

Three usual causes:

  1. Seed didn't run. Verify:

    docker exec qasl-test-postgres psql -U qasl_admin -d qasl_security -c "SELECT COUNT(*) FROM findings;"

    If 0, re-run step 5.5.

  2. Datasource lost. Grafana → Configuration → Data Sources → QASL-PostgreSQLTest. Should return "Database Connection OK".

  3. Time range too narrow. Top-right time picker → Last 30 days.

seed_demo.py fails on Windows with UnicodeEncodeError

Python 3.13 on Windows defaults stdout to cp1252. Fix:

$env:PYTHONIOENCODING = "utf-8"
python seed\seed_demo.py

Normalizer container restarting in a loop

docker compose logs normalizer --tail=100

Most common: import error in main.py (new module not in requirements.txt) or postgres still booting. Wait ~60 s on first boot; if it persists, rebuild:

docker compose up -d --build normalizer

PDF generator fails with "missing FROM-clause entry"

Pre-existing query bug fixed in v1.1. The view v_active_findings already exposes project_name and business_criticality — don't add JOIN projects p. The current query is SELECT * FROM v_active_findings WHERE project_name = $1.

Reset everything (⚠️ deletes all data)

docker compose down -v
rm -rf pdf-generator/reports/*.pdf

Then start over from step 5.3.


🗺️ 12. Roadmap

Sprint Scope
Sprint 1 Stack, normalizer, CFQI, Grafana, PDF generator (this release)
Sprint 2 GitHub Actions integration; live tool runs against vulnerable demo apps (Juice Shop, DVWA)
Sprint 3 Slack notifications, Jira integration, SLA enforcement
Sprint 4 OWASP Dependency-Track integration as parallel SBOM authority
Sprint 5 Risk acceptance workflow, exception management, IAST integration (Contrast Community)
Sprint 6 Public demo VM with HTTPS + Caddy reverse proxy + URL sharing

👤 13. Author, license & disclaimer

Elyer MaldonadoAI Quality & Risk Architect

License

MIT — see LICENSE.

Disclaimer

This framework is for defensive security purposes: identifying vulnerabilities in your own systems before attackers do. Do not use against systems you don't own or have explicit authorization to test. Authors are not responsible for misuse.


QASL ECOSYSTEM · MATHCORE + INGRID + QASL TEST SECURITY OWASP Top 10 2021 · OWASP ASVS 4.0 · NIST SP 800-218 (SSDF) · ISO/IEC 27001

About

Forensic-grade AppSec testing framework with CFQI scoring algorithm (Code Forensic Quality Index v1.0). Docker stack: PostgreSQL + FastAPI + Grafana. Unifies SAST · SCA · DAST · Secrets · IaC, emits two forensic PDFs (executive + dictamen pericial). Sibling of INGRID (AFQI). By Elyer Maldonado — AI Quality & Risk Architect.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors