diff --git a/cyberguard/.env.example b/cyberguard/.env.example new file mode 100644 index 0000000..8716076 --- /dev/null +++ b/cyberguard/.env.example @@ -0,0 +1,13 @@ +CYBERGUARD_API_URL=http://localhost:8000 +CYBERGUARD_API_TOKEN=replace-me +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_DB=cyberguard +POSTGRES_USER=cyberguard +POSTGRES_PASSWORD=cyberguard +NEO4J_URI=bolt://localhost:7687 +NEO4J_USER=neo4j +NEO4J_PASSWORD=cyberguard123 +REDIS_URL=redis://localhost:6379/0 +KAFKA_BOOTSTRAP_SERVERS=localhost:9092 +ELASTICSEARCH_URL=http://localhost:9200 diff --git a/cyberguard/.github/CODEOWNERS b/cyberguard/.github/CODEOWNERS new file mode 100644 index 0000000..66ae423 --- /dev/null +++ b/cyberguard/.github/CODEOWNERS @@ -0,0 +1 @@ +* @SoftwareDevLabs/security-platform diff --git a/cyberguard/.github/workflows/ci.yml b/cyberguard/.github/workflows/ci.yml new file mode 100644 index 0000000..500e2c0 --- /dev/null +++ b/cyberguard/.github/workflows/ci.yml @@ -0,0 +1,13 @@ +name: CyberGuard CI +on: + pull_request: + push: +jobs: + sbom-scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./integrations/cicd/github-actions/sbom-action + with: + api-url: ${{ secrets.CYBERGUARD_API_URL }} + token: ${{ secrets.CYBERGUARD_API_TOKEN }} diff --git a/cyberguard/.github/workflows/deploy.yml b/cyberguard/.github/workflows/deploy.yml new file mode 100644 index 0000000..256b85d --- /dev/null +++ b/cyberguard/.github/workflows/deploy.yml @@ -0,0 +1,10 @@ +name: Deploy +on: + workflow_dispatch: +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Placeholder deployment + run: echo "Deploy CyberGuard stack" diff --git a/cyberguard/.github/workflows/fuzz-test.yml b/cyberguard/.github/workflows/fuzz-test.yml new file mode 100644 index 0000000..624d28c --- /dev/null +++ b/cyberguard/.github/workflows/fuzz-test.yml @@ -0,0 +1,12 @@ +name: Scheduled Fuzz Test +on: + schedule: + - cron: '0 2 * * *' + workflow_dispatch: +jobs: + fuzz: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Placeholder fuzz run + run: echo "Run boofuzz/radamsa orchestrator here" diff --git a/cyberguard/.github/workflows/sbom-scan.yml b/cyberguard/.github/workflows/sbom-scan.yml new file mode 100644 index 0000000..f769a2a --- /dev/null +++ b/cyberguard/.github/workflows/sbom-scan.yml @@ -0,0 +1,14 @@ +name: SBOM Vulnerability Scan +on: + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./integrations/cicd/github-actions/sbom-action + with: + api-url: ${{ secrets.CYBERGUARD_API_URL }} + token: ${{ secrets.CYBERGUARD_API_TOKEN }} diff --git a/cyberguard/LICENSE b/cyberguard/LICENSE new file mode 100644 index 0000000..99d9516 --- /dev/null +++ b/cyberguard/LICENSE @@ -0,0 +1,7 @@ +MIT License + +Copyright (c) 2026 CyberGuard + +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. diff --git a/cyberguard/README.md b/cyberguard/README.md new file mode 100644 index 0000000..49547d4 --- /dev/null +++ b/cyberguard/README.md @@ -0,0 +1,33 @@ +# CyberGuard + +CyberGuard is a modular AI-powered cybersecurity agent platform for mission-critical systems. + +## Architecture + +```mermaid +flowchart LR + CLI[CLI] --> API[FastAPI API Gateway] + Dashboard[React Dashboard] --> API + Agent[LangGraph Agent] --> API + API --> PG[(PostgreSQL)] + API --> N4J[(Neo4j)] + API --> REDIS[(Redis)] + API --> ES[(Elasticsearch)] + API --> KAFKA[(Kafka)] +``` + +## Quick start + +```bash +docker compose up -d +``` + +## Modules + +- `packages/domain-engine/` — Domain selector + standards mapper +- `integrations/cicd/github-actions/sbom-action/` — reusable SBOM CI action +- `infra/docker/docker-compose.yml` — local runtime stack + +## Demo data + +Sample SBOM/CVE fixtures are expected under `tests/fixtures/`. diff --git a/cyberguard/apps/agent/.gitkeep b/cyberguard/apps/agent/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/apps/agent/graph.py b/cyberguard/apps/agent/graph.py new file mode 100644 index 0000000..fd1e2a2 --- /dev/null +++ b/cyberguard/apps/agent/graph.py @@ -0,0 +1,23 @@ +NODES = [ + "domain_selector", + "sbom_analyst", + "vulnerability_researcher", + "risk_assessor", + "compliance_checker", + "remediation_planner", + "report_generator", + "incident_responder", +] + +TOOLS = [ + "SearchVulnerabilitiesTool", + "GetSBOMTool", + "ComputeRiskScoreTool", + "CheckComplianceTool", + "GenerateReportTool", + "CreateIncidentTool", + "QueryKnowledgeGraphTool", + "TriggerPentestTool", + "GetArtifactTool", + "SendNotificationTool", +] diff --git a/cyberguard/apps/api-gateway/.gitkeep b/cyberguard/apps/api-gateway/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/apps/api-gateway/main.py b/cyberguard/apps/api-gateway/main.py new file mode 100644 index 0000000..8760142 --- /dev/null +++ b/cyberguard/apps/api-gateway/main.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from pydantic import BaseModel + +from domain_engine import DomainType + +try: + from fastapi import FastAPI +except ImportError: # pragma: no cover + FastAPI = None + + +class RiskScoreRequest(BaseModel): + sbom_id: str + domain: DomainType + asset_context: dict = {} + + +if FastAPI: + app = FastAPI(title="CyberGuard API Gateway", version="0.1.0") + + @app.get("/health") + def health() -> dict[str, str]: + return {"status": "ok"} + + @app.post("/api/v1/risk/score") + def score_risk(request: RiskScoreRequest) -> dict: + return {"sbom_id": request.sbom_id, "domain": request.domain, "scores": []} +else: + app = None diff --git a/cyberguard/apps/cli/main.py b/cyberguard/apps/cli/main.py new file mode 100644 index 0000000..49d3477 --- /dev/null +++ b/cyberguard/apps/cli/main.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +try: + import typer +except ImportError: # pragma: no cover + typer = None + + +if typer: + app = typer.Typer(name="cyberguard") + + sbom_app = typer.Typer() + vuln_app = typer.Typer() + risk_app = typer.Typer() + compliance_app = typer.Typer() + incident_app = typer.Typer() + pentest_app = typer.Typer() + fuzz_app = typer.Typer() + agent_app = typer.Typer() + + @sbom_app.command("generate") + def sbom_generate(source: str = ".", output: str = "sbom.cdx.json", format: str = "cyclonedx") -> None: + typer.echo(f"generate {format} from {source} -> {output}") + + @sbom_app.command("submit") + def sbom_submit(file: str, product: str, version: str, domain: str) -> None: + typer.echo(f"submit {file} for {product}@{version} ({domain})") + + @sbom_app.command("scan") + def sbom_scan(sbom_id: str, threshold: str = "HIGH", fail_on_critical: bool = False) -> None: + typer.echo(f"scan {sbom_id} threshold={threshold} fail_on_critical={fail_on_critical}") + + @sbom_app.command("diff") + def sbom_diff(from_id: str = typer.Option(..., "--from"), to: str = typer.Option(..., "--to")) -> None: + typer.echo(f"diff {from_id} -> {to}") + + @vuln_app.command("search") + def vuln_search(cve: str) -> None: + typer.echo(f"search {cve}") + + @vuln_app.command("report") + def vuln_report(sbom_id: str, output: str, format: str = "pdf") -> None: + typer.echo(f"report for {sbom_id} -> {output} ({format})") + + @risk_app.command("score") + def risk_score(sbom_id: str, domain: str, asil: str | None = None) -> None: + typer.echo(f"risk score {sbom_id} domain={domain} asil={asil}") + + @compliance_app.command("check") + def compliance_check(standard: str, product: str) -> None: + typer.echo(f"check {standard} for {product}") + + @compliance_app.command("report") + def compliance_report(standard: str, output: str) -> None: + typer.echo(f"report {standard} -> {output}") + + @incident_app.command("create") + def incident_create(severity: str, description: str, sbom_id: str) -> None: + typer.echo(f"incident severity={severity} sbom={sbom_id}: {description}") + + @pentest_app.command("run") + def pentest_run(target: str, type: str = "api-scan") -> None: + typer.echo(f"pentest {type} on {target}") + + @fuzz_app.command("run") + def fuzz_run(target: str, protocol: str, duration: int = 3600) -> None: + typer.echo(f"fuzz {protocol} on {target} for {duration}s") + + @agent_app.command("chat") + def agent_chat() -> None: + typer.echo("Starting interactive agent chat...") + + @agent_app.command("ask") + def agent_ask(question: str) -> None: + typer.echo(question) + + app.add_typer(sbom_app, name="sbom") + app.add_typer(vuln_app, name="vuln") + app.add_typer(risk_app, name="risk") + app.add_typer(compliance_app, name="compliance") + app.add_typer(incident_app, name="incident") + app.add_typer(pentest_app, name="pentest") + app.add_typer(fuzz_app, name="fuzz") + app.add_typer(agent_app, name="agent") +else: + app = None + + +if __name__ == "__main__" and typer: + app() diff --git a/cyberguard/apps/dashboard/.gitkeep b/cyberguard/apps/dashboard/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docker-compose.yml b/cyberguard/docker-compose.yml new file mode 100644 index 0000000..53f9fb9 --- /dev/null +++ b/cyberguard/docker-compose.yml @@ -0,0 +1,2 @@ +include: + - ./infra/docker/docker-compose.yml diff --git a/cyberguard/docs/api/.gitkeep b/cyberguard/docs/api/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docs/api/openapi.yaml b/cyberguard/docs/api/openapi.yaml new file mode 100644 index 0000000..98bb119 --- /dev/null +++ b/cyberguard/docs/api/openapi.yaml @@ -0,0 +1,11 @@ +openapi: 3.1.0 +info: + title: CyberGuard API + version: 0.1.0 +paths: + /api/v1/sbom/submit: + post: + summary: Submit an SBOM for normalization and scanning + responses: + '202': + description: Accepted diff --git a/cyberguard/docs/architecture/.gitkeep b/cyberguard/docs/architecture/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docs/architecture/data-flow.md b/cyberguard/docs/architecture/data-flow.md new file mode 100644 index 0000000..f1e3f07 --- /dev/null +++ b/cyberguard/docs/architecture/data-flow.md @@ -0,0 +1,3 @@ +# Data Flow + +SBOMs enter via CLI/API, are normalized and scanned, vulnerabilities are enriched, risk/compliance are computed, and incidents are tracked. diff --git a/cyberguard/docs/architecture/domain-model.md b/cyberguard/docs/architecture/domain-model.md new file mode 100644 index 0000000..7e39707 --- /dev/null +++ b/cyberguard/docs/architecture/domain-model.md @@ -0,0 +1,3 @@ +# Domain Model + +Core entities: Organization, Product, SBOM, Vulnerability, RiskAssessment, ComplianceReport, CybersecurityIncident. diff --git a/cyberguard/docs/architecture/overview.md b/cyberguard/docs/architecture/overview.md new file mode 100644 index 0000000..157e7eb --- /dev/null +++ b/cyberguard/docs/architecture/overview.md @@ -0,0 +1,3 @@ +# CyberGuard Architecture Overview + +CyberGuard uses a modular package-based architecture with API, dashboard, and AI agent applications backed by PostgreSQL, Neo4j, Redis, Kafka, and Elasticsearch. diff --git a/cyberguard/docs/standards/aerospace/.gitkeep b/cyberguard/docs/standards/aerospace/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docs/standards/aerospace/do326a.md b/cyberguard/docs/standards/aerospace/do326a.md new file mode 100644 index 0000000..140a320 --- /dev/null +++ b/cyberguard/docs/standards/aerospace/do326a.md @@ -0,0 +1,3 @@ +# DO-326A / ED-202A + +Airworthiness security process alignment notes. diff --git a/cyberguard/docs/standards/automotive/.gitkeep b/cyberguard/docs/standards/automotive/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docs/standards/automotive/iso21434.md b/cyberguard/docs/standards/automotive/iso21434.md new file mode 100644 index 0000000..a80c8d8 --- /dev/null +++ b/cyberguard/docs/standards/automotive/iso21434.md @@ -0,0 +1,3 @@ +# ISO/SAE 21434 + +Automotive cybersecurity engineering mapping guidance. diff --git a/cyberguard/docs/standards/automotive/iso8800-ai-safety.md b/cyberguard/docs/standards/automotive/iso8800-ai-safety.md new file mode 100644 index 0000000..458d6d3 --- /dev/null +++ b/cyberguard/docs/standards/automotive/iso8800-ai-safety.md @@ -0,0 +1,3 @@ +# ISO 8800 AI Safety + +AI safety mapping for road vehicle cybersecurity workflows. diff --git a/cyberguard/docs/standards/automotive/unece-r155-csms.md b/cyberguard/docs/standards/automotive/unece-r155-csms.md new file mode 100644 index 0000000..f4d0615 --- /dev/null +++ b/cyberguard/docs/standards/automotive/unece-r155-csms.md @@ -0,0 +1,3 @@ +# UNECE R155 / CSMS + +Cyber Security Management System evidence mapping. diff --git a/cyberguard/docs/standards/automotive/unece-r156-sums.md b/cyberguard/docs/standards/automotive/unece-r156-sums.md new file mode 100644 index 0000000..c7e582c --- /dev/null +++ b/cyberguard/docs/standards/automotive/unece-r156-sums.md @@ -0,0 +1,3 @@ +# UNECE R156 / SUMS + +Software update management mapping and OTA artifact references. diff --git a/cyberguard/docs/standards/common/.gitkeep b/cyberguard/docs/standards/common/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docs/standards/common/ai-governance.md b/cyberguard/docs/standards/common/ai-governance.md new file mode 100644 index 0000000..bf8edba --- /dev/null +++ b/cyberguard/docs/standards/common/ai-governance.md @@ -0,0 +1,3 @@ +# AI Governance + +Cross-domain AI governance and assurance guidance. diff --git a/cyberguard/docs/standards/common/iso27001-isms.md b/cyberguard/docs/standards/common/iso27001-isms.md new file mode 100644 index 0000000..0532384 --- /dev/null +++ b/cyberguard/docs/standards/common/iso27001-isms.md @@ -0,0 +1,3 @@ +# ISO/IEC 27001 ISMS + +Common ISMS controls used across all domains. diff --git a/cyberguard/docs/standards/medical/.gitkeep b/cyberguard/docs/standards/medical/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docs/standards/medical/fda-cybersecurity.md b/cyberguard/docs/standards/medical/fda-cybersecurity.md new file mode 100644 index 0000000..1688cb2 --- /dev/null +++ b/cyberguard/docs/standards/medical/fda-cybersecurity.md @@ -0,0 +1,3 @@ +# FDA Cybersecurity Guidance + +Medical device cybersecurity process references. diff --git a/cyberguard/docs/standards/medical/iec62443.md b/cyberguard/docs/standards/medical/iec62443.md new file mode 100644 index 0000000..6653b1f --- /dev/null +++ b/cyberguard/docs/standards/medical/iec62443.md @@ -0,0 +1,3 @@ +# IEC 62443 + +Industrial/medical cybersecurity control mapping. diff --git a/cyberguard/docs/standards/railway/.gitkeep b/cyberguard/docs/standards/railway/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/docs/standards/railway/iec62280.md b/cyberguard/docs/standards/railway/iec62280.md new file mode 100644 index 0000000..796ff6c --- /dev/null +++ b/cyberguard/docs/standards/railway/iec62280.md @@ -0,0 +1,3 @@ +# IEC 62280 + +Railway communication cybersecurity requirements mapping. diff --git a/cyberguard/infra/docker/docker-compose.dev.yml b/cyberguard/infra/docker/docker-compose.dev.yml new file mode 100644 index 0000000..603fc6f --- /dev/null +++ b/cyberguard/infra/docker/docker-compose.dev.yml @@ -0,0 +1,7 @@ +services: + api-gateway: + environment: + ENV: development + dashboard: + environment: + NODE_ENV: development diff --git a/cyberguard/infra/docker/docker-compose.yml b/cyberguard/infra/docker/docker-compose.yml new file mode 100644 index 0000000..7858a64 --- /dev/null +++ b/cyberguard/infra/docker/docker-compose.yml @@ -0,0 +1,41 @@ +services: + api-gateway: + image: python:3.12-slim + command: ["python", "-m", "http.server", "8000"] + ports: ["8000:8000"] + dashboard: + image: node:20-alpine + command: ["sh", "-c", "npm i -g serve && serve -l 3000"] + ports: ["3000:3000"] + postgres: + image: postgres:16 + environment: + POSTGRES_PASSWORD: cyberguard + neo4j: + image: neo4j:5 + environment: + NEO4J_AUTH: neo4j/cyberguard123 + redis: + image: redis:7 + zookeeper: + image: confluentinc/cp-zookeeper:7.6.1 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + kafka: + image: confluentinc/cp-kafka:7.6.1 + depends_on: [zookeeper] + environment: + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.13.4 + environment: + discovery.type: single-node + xpack.security.enabled: "false" + agent: + image: python:3.12-slim + command: ["python", "-m", "http.server", "8100"] + vuln-feed-worker: + image: python:3.12-slim + command: ["python", "-c", "import time; print('worker running'); time.sleep(3600)"] diff --git a/cyberguard/infra/k8s/deployments/.gitkeep b/cyberguard/infra/k8s/deployments/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/infra/k8s/namespace.yaml b/cyberguard/infra/k8s/namespace.yaml new file mode 100644 index 0000000..adc0b2f --- /dev/null +++ b/cyberguard/infra/k8s/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: cyberguard diff --git a/cyberguard/infra/k8s/services/.gitkeep b/cyberguard/infra/k8s/services/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/infra/terraform/.gitkeep b/cyberguard/infra/terraform/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/infra/terraform/main.tf b/cyberguard/infra/terraform/main.tf new file mode 100644 index 0000000..7025600 --- /dev/null +++ b/cyberguard/infra/terraform/main.tf @@ -0,0 +1,5 @@ +terraform { + required_version = ">= 1.5.0" +} + +provider "null" {} diff --git a/cyberguard/infra/terraform/variables.tf b/cyberguard/infra/terraform/variables.tf new file mode 100644 index 0000000..fafa6c2 --- /dev/null +++ b/cyberguard/infra/terraform/variables.tf @@ -0,0 +1,4 @@ +variable "environment" { + type = string + default = "dev" +} diff --git a/cyberguard/integrations/cicd/github-actions/sbom-action/action.yml b/cyberguard/integrations/cicd/github-actions/sbom-action/action.yml new file mode 100644 index 0000000..9308862 --- /dev/null +++ b/cyberguard/integrations/cicd/github-actions/sbom-action/action.yml @@ -0,0 +1,34 @@ +name: CyberGuard SBOM Action +description: Generate, submit, and scan CycloneDX SBOMs +inputs: + api-url: + required: true + description: CyberGuard API base URL + token: + required: true + description: API auth token + severity-threshold: + required: false + default: CRITICAL +runs: + using: composite + steps: + - name: Detect project type + shell: bash + run: | + if [ -f package.json ]; then echo "PROJECT=node" >> $GITHUB_ENV; fi + if [ -f pyproject.toml ] || [ -f requirements.txt ]; then echo "PROJECT=python" >> $GITHUB_ENV; fi + - name: Generate CycloneDX SBOM + shell: bash + run: | + if [ "$PROJECT" = "node" ]; then npx @cyclonedx/cdxgen -o sbom.cdx.json; else syft . -o cyclonedx-json=sbom.cdx.json; fi + - name: Validate and submit SBOM + shell: bash + env: + INPUT_TOKEN: ${{ inputs.token }} + run: | + test -s sbom.cdx.json + curl -fsSL -X POST "${{ inputs.api-url }}/api/v1/sbom/submit" \ + -H "X-API-Token: $INPUT_TOKEN" \ + -H "Content-Type: application/json" \ + --data @sbom.cdx.json diff --git a/cyberguard/integrations/cicd/gitlab-ci/.gitkeep b/cyberguard/integrations/cicd/gitlab-ci/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/cicd/jenkins/.gitkeep b/cyberguard/integrations/cicd/jenkins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/pentest/boofuzz/.gitkeep b/cyberguard/integrations/pentest/boofuzz/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/pentest/owasp-zap/.gitkeep b/cyberguard/integrations/pentest/owasp-zap/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/pentest/radamsa/.gitkeep b/cyberguard/integrations/pentest/radamsa/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/sbom-importers/cyclonedx/.gitkeep b/cyberguard/integrations/sbom-importers/cyclonedx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/sbom-importers/spdx/.gitkeep b/cyberguard/integrations/sbom-importers/spdx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/sbom-importers/swid/.gitkeep b/cyberguard/integrations/sbom-importers/swid/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/vuln-feeds/cve/.gitkeep b/cyberguard/integrations/vuln-feeds/cve/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/vuln-feeds/nvd/.gitkeep b/cyberguard/integrations/vuln-feeds/nvd/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/vuln-feeds/osv/.gitkeep b/cyberguard/integrations/vuln-feeds/osv/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/integrations/vuln-feeds/vendor-advisories/.gitkeep b/cyberguard/integrations/vuln-feeds/vendor-advisories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/tests/e2e/.gitkeep b/cyberguard/tests/e2e/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/tests/fixtures/sbom-samples/.gitkeep b/cyberguard/tests/fixtures/sbom-samples/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/tests/fixtures/vuln-samples/.gitkeep b/cyberguard/tests/fixtures/vuln-samples/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/tests/integration/.gitkeep b/cyberguard/tests/integration/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cyberguard/tests/unit/test_domain_engine.py b/cyberguard/tests/unit/test_domain_engine.py new file mode 100644 index 0000000..cc62de9 --- /dev/null +++ b/cyberguard/tests/unit/test_domain_engine.py @@ -0,0 +1,32 @@ +from pathlib import Path +import sys + +ROOT = Path(__file__).resolve().parents[2] +DOMAIN_ENGINE_PATH = ROOT / "packages" / "domain-engine" +if str(DOMAIN_ENGINE_PATH) not in sys.path: + sys.path.insert(0, str(DOMAIN_ENGINE_PATH)) + +from domain_engine import DomainSelector, DomainType, StandardsMapper + + +def test_domain_selector_loads_automotive_profile() -> None: + selector = DomainSelector() + + profile = selector.select_domain(DomainType.AUTOMOTIVE) + + assert profile.domain == DomainType.AUTOMOTIVE + assert any(standard.id == "iso21434" for standard in profile.standards) + assert profile.risk_scoring_parameters["asil"] == "A/B/C/D" + assert "ota-abuse" in profile.threat_categories + + +def test_standards_mapper_returns_requirements_and_templates() -> None: + mapper = StandardsMapper() + + requirements = mapper.get_compliance_requirements("unece-r155") + templates = mapper.get_artifact_templates(DomainType.AUTOMOTIVE, "unece-r155") + + assert "Article 7 CSMS governance evidence" in requirements.requirements + template_ids = {template.template_id for template in templates} + assert "cyber-plan" in template_ids + assert "r155-notification" in template_ids diff --git a/cyberguard/tests/unit/test_risk_engine.py b/cyberguard/tests/unit/test_risk_engine.py new file mode 100644 index 0000000..b911e8e --- /dev/null +++ b/cyberguard/tests/unit/test_risk_engine.py @@ -0,0 +1,36 @@ +from pathlib import Path +import sys + +ROOT = Path(__file__).resolve().parents[2] +for package_dir in [ + ROOT / "packages" / "domain-engine", + ROOT / "packages" / "risk-engine", +]: + if str(package_dir) not in sys.path: + sys.path.insert(0, str(package_dir)) + +from domain_engine import DomainType +from risk_engine.engine import contextual_risk_score + + +def test_automotive_contextual_risk_score_uses_asil_weight() -> None: + score = contextual_risk_score( + domain=DomainType.AUTOMOTIVE, + cvss_v3=8.0, + reachability_score=1.2, + exposure_factor=1.1, + asil="D", + ) + + assert score == 31.68 + + +def test_medical_contextual_risk_score_uses_sil_weight() -> None: + score = contextual_risk_score( + domain=DomainType.MEDICAL, + cvss_v3=7.0, + reachability_score=1.0, + sil=3, + ) + + assert score == 14.0