Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
2f6e3a1
Improve API client error handling and logging
srujan0301 Mar 20, 2026
c243817
Fix API logging and improve error handling in fetchWithAuth
srujan0301 Mar 22, 2026
6eb1462
Improve frontend API error handling and request consistency
srujan0301 Mar 29, 2026
35a1601
Refactor API handling and improve frontend request consistency
srujan0301 Mar 29, 2026
fd6bf4e
Refactor auth components with Tailwind
Heet27 Mar 29, 2026
2ab110e
Updated LoginPage and SignupFormPanel with Tailwind layout
Heet27 Mar 29, 2026
6736f4c
Final cleanup: fixed layout and removed duplicates
Heet27 Mar 29, 2026
2ec272f
feat: TailwindCSS migration for About Us and Contact Us pages, update…
Brook4747 Mar 31, 2026
da9488a
removed .hidden class style due to tailwind
liyunze-coding Apr 2, 2026
4905883
fixed tailwindv4 issue
liyunze-coding Apr 2, 2026
47d7664
Refactor auth components with Tailwind
Heet27 Mar 29, 2026
2a7d437
Updated LoginPage and SignupFormPanel with Tailwind layout
Heet27 Mar 29, 2026
9d24dcf
Final cleanup: fixed layout and removed duplicates
Heet27 Mar 29, 2026
6928df8
Merge branch 'feature/tailwind-heet' of https://github.com/Hardhat-En…
liyunze-coding Apr 2, 2026
68d96ce
Merge pull request #135 from Hardhat-Enterprises/feature/tailwind-heet
liyunze-coding Apr 2, 2026
100f22a
Merge pull request #137 from Hardhat-Enterprises/feature/tailwindcss-…
liyunze-coding Apr 2, 2026
16231c9
removed unnecessary files
liyunze-coding Apr 2, 2026
b04a1d7
Migrated css to tailwindcss on frontend page Settings
Apr 2, 2026
7e78c64
feat: migrate Connections folder to TailwindCSS
Brook4747 Apr 3, 2026
fd7641d
Improved API client with timeout, logging, and error handling
srujan0301 Apr 5, 2026
0955dd3
Resolve merge conflict by moving API client changes to client.ts
srujan0301 Apr 5, 2026
134f7a1
Refactored Dropdown component from CSS to Tailwind and integrated int…
NYASWA1014 Apr 5, 2026
a7c4070
Removed unused Dropdown backup file
NYASWA1014 Apr 5, 2026
bde49d6
Adjusted dropdown styling to align with existing UI design
NYASWA1014 Apr 5, 2026
a80b106
migrated scans folder to TailwindCSS
liyunze-coding Apr 5, 2026
38bfc3a
implement scan backend
phanvuminhtrung Apr 6, 2026
b4c7084
Migrate Auth pages styling to TailwindCSS
SEBINSHASAMEERSHA Apr 6, 2026
615d7d8
Migrate DashBoard page styling to TailwindCSS
AAMIRAU Apr 6, 2026
5d97cd0
migrate Landing Page + Component to using Tailwind.css
phanvuminhtrung Apr 7, 2026
9a30616
Removing redundant css file
phanvuminhtrung Apr 7, 2026
47782e3
refactor all component using LandingPage.css
phanvuminhtrung Apr 7, 2026
7f74e2e
fixing PR suggestion: restore hash target offset for sticky header
phanvuminhtrung Apr 7, 2026
f7a41f9
Refined dropdown styling to match original UI
NYASWA1014 Apr 7, 2026
36aab7c
Resolve merge conflict and finalize dropdown styling
NYASWA1014 Apr 7, 2026
7944c0b
Migrate LoginHeader styling to TailwindCSS
SEBINSHASAMEERSHA Apr 7, 2026
631384b
Write structural consistency tests for metadata, rego and collectors
du-dhartley Apr 8, 2026
2b3518c
Refactor to meet branching strategy, comment out deploy for now, fire…
du-dhartley Apr 8, 2026
e4968ea
Update metadata to use a filename that doesn't exist to fail the CI test
du-dhartley Apr 8, 2026
ca1d8c2
Render output in readable format
du-dhartley Apr 8, 2026
c1341a7
Add the python script I forgot to commit
du-dhartley Apr 8, 2026
80c84a1
Comment out longer running test so we can focus on consistency checker
du-dhartley Apr 8, 2026
8741de6
Ensure test 5 is a hard fail, not an expected fail
du-dhartley Apr 8, 2026
4f6dccc
Remove collectors that are redundant - replaced by newer collectors a…
du-dhartley Apr 8, 2026
dcf4df5
Revert force-fail filename change
du-dhartley Apr 8, 2026
73fe8b9
Uncomment analyze and lint steps
du-dhartley Apr 8, 2026
d510c86
feat: migrate Evidence page to TailwindCSS
vhthai-max Apr 9, 2026
07edd1f
Merge pull request #157 from Hardhat-Enterprises/feature/tailwindcss-…
liyunze-coding Apr 9, 2026
f129e6a
Merge branch 'tailwindcss-main' into feature/tailwindcss-peibing
Apr 9, 2026
8644ba1
put class names in element attributes directly
Apr 9, 2026
26725a3
feat: Changed and fixed the previous layout issues
Brook4747 Apr 10, 2026
83adb9c
Resolve merge conflict in GoogleCallbackPage
SEBINSHASAMEERSHA Apr 10, 2026
7ff6d84
Refine Auth Tailwind styling to better match original UI
SEBINSHASAMEERSHA Apr 10, 2026
1b080d0
Merge pull request #155 from Hardhat-Enterprises/feature/tailwindcss-…
liyunze-coding Apr 11, 2026
32ad312
Refine dashboard gradients and preserve original styling in Tailwind …
AAMIRAU Apr 11, 2026
b331fd9
Migrate ContactAdminPage from CSS to TailwindCSS
srujan0301 Apr 12, 2026
a323752
Merge pull request #156 from Hardhat-Enterprises/feature/tailwindcss-…
liyunze-coding Apr 12, 2026
6789492
feat(scans): add GET /scans/{id}/summary endpoint
Ashjani Apr 13, 2026
e280815
Merge pull request #140 from Hardhat-Enterprises/feature/tailwindcss-…
peibingg Apr 14, 2026
b4a4b61
Merge pull request #162 from Hardhat-Enterprises/feature/tailwindcss/…
liyunze-coding Apr 15, 2026
9e7651c
Merge branch 'main' into tailwindcss-main
liyunze-coding Apr 15, 2026
aa965d8
feat: add password strength indicator using Tailwind CSS
Apr 16, 2026
828c430
Merge pull request #170 from Hardhat-Enterprises/feat/password-streng…
liyunze-coding Apr 16, 2026
79eaead
fix(evidence): address PR #161 review feedback
vhthai-max Apr 17, 2026
8ac3817
Merge pull request #161 from vhthai-max/feature/tailwind-evidence-jay
liyunze-coding Apr 17, 2026
e808fdc
simplified tailwindcss classes
liyunze-coding Apr 17, 2026
0fa9f4b
adding tailwind migration note
liyunze-coding Apr 19, 2026
0348acc
Remove unintended file from repository
NYASWA1014 Apr 19, 2026
3eaf28f
Merge pull request #153 from JOHNNYAKUNDI-LAB/feature/dropdown-tailwi…
liyunze-coding Apr 19, 2026
6df2b9e
Merge branch 'tailwindcss-main' of https://github.com/Hardhat-Enterpr…
liyunze-coding Apr 19, 2026
8ccef7d
Merge branch 'main' into feature/26t1-imp-dha-004-scanning-engine-ci-…
du-dhartley Apr 21, 2026
a6e4c0d
Merge pull request #160 from Hardhat-Enterprises/feature/26t1-imp-dha…
du-dhartley Apr 21, 2026
de91f76
Merge branch 'main' into tailwindcss-main
liyunze-coding Apr 22, 2026
6fc6bd9
Merge branch 'tailwindcss-main' into feature/tailwindcss-srujan
liyunze-coding Apr 22, 2026
379df84
fixed tailwindcss classes admin page
liyunze-coding Apr 22, 2026
2b4459e
reverted client ts to original state
liyunze-coding Apr 22, 2026
6cba675
Merge pull request #186 from Hardhat-Enterprises/feature/tailwindcss-…
liyunze-coding Apr 22, 2026
26420b3
Updated Account Page to TailwindCSS
liyunze-coding Apr 23, 2026
3bf002a
Merge pull request #189 from Hardhat-Enterprises/feature/tailwindcss-…
liyunze-coding Apr 23, 2026
45e0e84
ComplianceChart to TailwindCSS
liyunze-coding Apr 23, 2026
b9f0daf
moved Sidebar.tsx to TailwindCSS
liyunze-coding Apr 23, 2026
b4f8511
remove App.css, consolidated styles into one CSS file.
liyunze-coding Apr 23, 2026
a254d70
update tailwindcss classes, button cursor pointer
liyunze-coding Apr 24, 2026
f37ba5a
updated TailwindCSS colour classes
liyunze-coding Apr 24, 2026
9fcac22
update to look more similar to main branch
liyunze-coding Apr 24, 2026
d4f9bf7
added Tailwind migration note in docs
liyunze-coding Apr 24, 2026
d000b47
fixed vitests
liyunze-coding Apr 24, 2026
fce4e30
removed unnecessary ESLint comments
liyunze-coding Apr 25, 2026
ff444f3
Fixed UI, updated TailwindCSS classes
liyunze-coding Apr 27, 2026
f087284
reverted security evidence UI change
liyunze-coding Apr 27, 2026
eff4358
fixing permision assign
phanvuminhtrung May 1, 2026
75c0776
Merge remote-tracking branch 'origin/main' into feature/prescan-readi…
phanvuminhtrung May 1, 2026
59607aa
fixing prescan warning instead of block scan.
phanvuminhtrung May 1, 2026
9dac505
fixing typo
phanvuminhtrung May 1, 2026
8086174
including myself as reviewer to early review/stop any conflict of fil…
phanvuminhtrung May 1, 2026
501cc80
standardised tailwindCSS colours classes
liyunze-coding May 1, 2026
9336279
Fixed line endings of worker entrypoint sh
liyunze-coding May 1, 2026
0b5644b
updated TailwindCSS doc
liyunze-coding May 1, 2026
6c9cbe2
Merge pull request #193 from Hardhat-Enterprises/refactor/docker-work…
liyunze-coding May 1, 2026
ab08cf9
reorganised TailwindCSS class order using RustyWind
liyunze-coding May 1, 2026
ff8a82c
feat(frontend): add RelativeTime component and wire into scans/dashboard
s224877838 May 4, 2026
3406135
Fixing mark failed readiness probes as unverified
phanvuminhtrung May 4, 2026
ea3ba40
Fixing ScanPage with migration of tailwinds. Removing ScansPage.css
phanvuminhtrung May 4, 2026
a8d4e7f
refactor
s224877838 May 4, 2026
1845d6d
Merge pull request #191 from Hardhat-Enterprises/tailwindcss-main
liyunze-coding May 4, 2026
28c567e
Merge pull request #206 from Hardhat-Enterprises/relative-time-format
liyunze-coding May 4, 2026
2aa49cf
bringing index.css from tailwind migration to align TSX ScanPage.
phanvuminhtrung May 4, 2026
b14570e
Merge main into feature/prescan-readinesscheck
phanvuminhtrung May 4, 2026
e8d4cc7
improve documentation with future and gap
phanvuminhtrung May 4, 2026
68a7850
remove codeowner
phanvuminhtrung May 5, 2026
e24bce3
gcp collector and rego files from TAWALI CHAPAU (s225172187)
SatyamDeakin May 5, 2026
3876f26
Merge pull request #198 from Hardhat-Enterprises/feature/prescan-read…
phanvuminhtrung May 5, 2026
1115139
GRC Framework for CIS Microsoft 365 Foundations Benchmark v6.0.1 – 2-…
SatyamDeakin May 5, 2026
94b7d7c
Improve mobile responsiveness for assigned pages
AAMIRAU May 7, 2026
41ce950
Normalize framework name and control_id when building OPA package path
SidharthSabu May 6, 2026
a633fc2
Update test_wiring._expected_package to match tasks.py normalisation
SidharthSabu May 7, 2026
79768a2
Merge pull request #213 from Hardhat-Enterprises/feature/mobile-respo…
liyunze-coding May 7, 2026
ea34511
Improve mobile responsiveness for assigned pages
SEBINSHASAMEERSHA May 8, 2026
fde746d
Clean up Scans mobile table classes
SEBINSHASAMEERSHA May 8, 2026
f5fa565
Fix mobile responsiveness lint issues
SEBINSHASAMEERSHA May 8, 2026
b865185
Merge pull request #228 from Hardhat-Enterprises/feature/mobile-respo…
liyunze-coding May 8, 2026
d4087ca
Merge pull request #209 from Hardhat-Enterprises/Satyam-GRC
SatyamDeakin May 12, 2026
e0820f9
Merge pull request #210 from Hardhat-Enterprises/fix/opa-package-path…
SidharthSabu May 12, 2026
7162cf0
Merge pull request #208 from Hardhat-Enterprises/tawali-gcp-files
SatyamDeakin May 12, 2026
c589c3a
Merge branch 'main' into feature/26T1-BAC-AA-scan-summary-endpoint
Ashjani May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sh text eol=lf
128 changes: 72 additions & 56 deletions .github/workflows/engine.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
name: "Engine CI/CD"

on:
push:
branches: ['main', 'staging', 'dev']
# push:
# branches: ['main', 'staging', 'dev']
pull_request:
branches: ['main', 'staging', 'dev']
schedule:
- cron: '32 23 * * 6'
branches: ['main']
# schedule:
Comment on lines +4 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore CI triggers for staging/dev and scheduled scans

This workflow now runs only for pull requests targeting main because push and schedule triggers were commented out and pull_request was narrowed to main. As a result, engine checks no longer run for staging/dev PRs or weekly scheduled security scans, so regressions and vulnerabilities in those flows will go untested.

Useful? React with 👍 / 👎.

# - cron: '32 23 * * 6'

jobs:
changes:
Expand All @@ -29,8 +29,6 @@ jobs:

analyze:
name: Security Analysis on (${{ matrix.language }})
needs: changes
if: github.event_name != 'push' || needs.changes.outputs.engine == 'true'
runs-on: ubuntu-latest
permissions:
security-events: write
Expand All @@ -55,11 +53,10 @@ jobs:
with:
category: "/language:${{matrix.language}}"


run-lint:
name: Linting Code
runs-on: ubuntu-latest
needs: [changes, analyze]
if: github.event_name != 'push' || needs.changes.outputs.engine == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -82,58 +79,77 @@ jobs:
VALIDATE_NATURAL_LANGUAGE: false
VALIDATE_MARKDOWN_PRETTIER: false

build-and-deploy:
name: Build and Deploy Engine
needs: [changes, analyze]
environment: ${{ github.ref_name == 'main' && 'prod' || github.ref_name }}
test:
name: Run Engine Tests
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
security-events: write
packages: write
actions: write

steps:
- name: Checkout source code
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Set environment name
id: set-env
run: |
if [ "${{ github.ref_name }}" == "main" ]; then
echo "ENV_NAME=prod" >> $GITHUB_OUTPUT
else
echo "ENV_NAME=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi

- name: Login to Docker Hub
uses: docker/login-action@v3
- name: Set up Python
uses: actions/setup-python@v6
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
python-version: '3.12'

- name: Build and tag Docker image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/engine:${{ steps.set-env.outputs.ENV_NAME }} \
--build-arg ENV=${{ steps.set-env.outputs.ENV_NAME }} \
-f engine/docker/engine.Dockerfile .
- name: Install dependencies
working-directory: engine
run: pip install -e ".[dev]"

- name: Push Docker image
run: |
docker push ${{ secrets.DOCKER_USERNAME }}/engine:${{ steps.set-env.outputs.ENV_NAME }}
- name: Run structural checks
working-directory: engine
run: pytest --tb=line tests/test_wiring.py

- name: Scan Docker image with Grype
uses: anchore/scan-action@v6
with:
image: ${{ secrets.DOCKER_USERNAME }}/engine:${{ steps.set-env.outputs.ENV_NAME }}
fail-build: false
output-format: sarif
output-file: grype-report.sarif

- name: Upload SARIF to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: grype-report.sarif

# build-and-deploy:
# name: Build and Deploy Engine
# needs: [changes, analyze]
# environment: ${{ github.ref_name == 'main' && 'prod' || github.ref_name }}
# runs-on: ubuntu-latest
# permissions:
# contents: read
# id-token: write
# security-events: write
# packages: write
# actions: write
#
# steps:
# - name: Checkout source code
# uses: actions/checkout@v4
# with:
# ref: ${{ github.ref }}
# - name: Set environment name
# id: set-env
# run: |
# if [ "${{ github.ref_name }}" == "main" ]; then
# echo "ENV_NAME=prod" >> $GITHUB_OUTPUT
# else
# echo "ENV_NAME=${{ github.ref_name }}" >> $GITHUB_OUTPUT
# fi
#
# - name: Login to Docker Hub
# uses: docker/login-action@v3
# with:
# username: ${{ secrets.DOCKER_USERNAME }}
# password: ${{ secrets.DOCKER_PASSWORD }}
#
# - name: Build and tag Docker image
# run: |
# docker build -t ${{ secrets.DOCKER_USERNAME }}/engine:${{ steps.set-env.outputs.ENV_NAME }} \
# --build-arg ENV=${{ steps.set-env.outputs.ENV_NAME }} \
# -f engine/docker/engine.Dockerfile .
#
# - name: Push Docker image
# run: |
# docker push ${{ secrets.DOCKER_USERNAME }}/engine:${{ steps.set-env.outputs.ENV_NAME }}
#
# - name: Scan Docker image with Grype
# uses: anchore/scan-action@v6
# with:
# image: ${{ secrets.DOCKER_USERNAME }}/engine:${{ steps.set-env.outputs.ENV_NAME }}
# fail-build: false
# output-format: sarif
# output-file: grype-report.sarif
#
# - name: Upload SARIF to GitHub Security tab
# uses: github/codeql-action/upload-sarif@v3
# with:
# sarif_file: grype-report.sarif
141 changes: 141 additions & 0 deletions backend-api/app/api/v1/scans.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from collections import defaultdict

from app.core.auth import get_current_user
from app.db.session import get_async_session
Expand All @@ -14,12 +15,21 @@
from app.schemas.scan import (
ScanCreate,
ScanCreatedResponse,
ControlCategoryBreakdown,
ScanListItem,
ScanReadinessCheck,
ScanReadinessResponse,
ScanRead,
ScanResultRead,
ScanSummary,
)
from app.services.benchmark_reader import get_file_reader
from app.services.celery_client import queue_scan
from app.services.encryption import decrypt
from app.services.scan_readiness import (
evaluate_scan_readiness,
extract_required_permissions,
)

router = APIRouter(prefix="/scans", tags=["Scans"])

Expand Down Expand Up @@ -132,6 +142,76 @@ async def list_scans(
)
return list(result.scalars().all())

# Get scan readiness status for a given M365 connection and benchmark. This is used by the frontend before starting a scan to validate the connection and provide feedback on any issues that might cause the scan to fail or have incomplete results.
@router.get("/readiness", response_model=ScanReadinessResponse)
async def get_scan_readiness(
m365_connection_id: int,
framework: str,
benchmark: str,
version: str,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_async_session),
) -> ScanReadinessResponse:
"""Validate whether a scan can run successfully before queueing it."""
# Readiness uses the saved M365 connection exactly as the user configured it.
result = await db.execute(
select(M365Connection).where(
M365Connection.id == m365_connection_id,
M365Connection.user_id == current_user.id,
M365Connection.is_active == True,
)
)
connection = result.scalar_one_or_none()
if not connection:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"M365 connection {m365_connection_id} not found or inactive",
)

# Benchmark metadata is the source of truth for which controls are runnable and which permissions those controls declare.
file_reader = get_file_reader()
try:
metadata = file_reader.get_benchmark_metadata(framework, benchmark, version)
except FileNotFoundError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Benchmark {framework}/{benchmark}/{version} not found",
)

if metadata.get("platform", "").lower() != "m365":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Benchmark platform '{metadata.get('platform')}' does not match M365 connection",
)

# The API layer only prepares inputs here. The actual readiness logic lives in scan_readiness.py so the route stays thin.
required_permissions = extract_required_permissions(metadata.get("controls", []))
readiness = await evaluate_scan_readiness(
tenant_id=connection.tenant_id,
client_id=connection.client_id,
client_secret=decrypt(connection.encrypted_client_secret),
required_permissions=required_permissions,
)

# Convert the service result into the response model returned to the frontend.
return ScanReadinessResponse(
ready=readiness.ready,
summary=readiness.summary,
required_permissions=readiness.required_permissions,
missing_permissions=readiness.missing_permissions,
unverified_permissions=readiness.unverified_permissions,
checks=[
ScanReadinessCheck(
key=check.key,
label=check.label,
status=check.status,
severity=check.severity,
message=check.message,
)
for check in readiness.checks
],
)


@router.get("/{scan_id}", response_model=ScanRead)
async def get_scan(
Expand All @@ -153,6 +233,67 @@ async def get_scan(
)
return scan

@router.get("/{scan_id}/summary", response_model=ScanSummary)
async def get_scan_summary(
scan_id: int,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_async_session),
) -> ScanSummary:
"""Get a lightweight summary for a scan."""
result = await db.execute(
select(Scan).where(
Scan.id == scan_id,
Scan.user_id == current_user.id,
)
)
scan = result.scalar_one_or_none()
if not scan:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Scan {scan_id} not found",
)

results = await db.execute(
select(ScanResult.control_id, ScanResult.status).where(
ScanResult.scan_id == scan_id
)
)
rows = results.all()

buckets: dict[str, dict[str, int]] = defaultdict(
lambda: {"total": 0, "passed": 0, "failed": 0, "skipped": 0, "error": 0}
)
for control_id, status_ in rows:
prefix = (
control_id.split(".")[0]
if "." in control_id
else control_id.split("-")[0]
)
buckets[prefix]["total"] += 1
if status_ in buckets[prefix]:
buckets[prefix][status_] += 1

categories = [
ControlCategoryBreakdown(category=cat, **counts)
for cat, counts in sorted(buckets.items())
]

return ScanSummary(
id=scan.id,
status=scan.status,
framework=scan.framework,
benchmark=scan.benchmark,
version=scan.version,
started_at=scan.started_at,
finished_at=scan.finished_at,
compliance_score=scan.compliance_score,
total_controls=scan.total_controls,
passed_count=scan.passed_count,
failed_count=scan.failed_count,
skipped_count=scan.skipped_count,
error_count=scan.error_count,
categories=categories,
)

@router.get("/{scan_id}/results", response_model=list[ScanResultRead])
async def get_scan_results(
Expand Down
51 changes: 51 additions & 0 deletions backend-api/app/schemas/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,54 @@ class ScanCreatedResponse(BaseModel):
id: int
status: str
message: str

class ControlCategoryBreakdown(BaseModel):
"""Pass/fail counts grouped by control category prefix."""

category: str
total: int
passed: int
failed: int
skipped: int
error: int


class ScanSummary(BaseModel):
"""Lightweight scan summary without full result detail."""

id: int
status: str
framework: str
benchmark: str
version: str
started_at: datetime
finished_at: datetime | None
compliance_score: Decimal | None
total_controls: int
passed_count: int
failed_count: int
skipped_count: int
error_count: int
categories: list[ControlCategoryBreakdown]

model_config = ConfigDict(from_attributes=True)

class ScanReadinessCheck(BaseModel):
"""Individual readiness check result."""

key: str
label: str
status: str # pass, fail, warn
severity: str # critical, warning
message: str


class ScanReadinessResponse(BaseModel):
"""Pre-scan readiness result."""

ready: bool
summary: str
required_permissions: list[str]
missing_permissions: list[str]
unverified_permissions: list[str]
checks: list[ScanReadinessCheck]
Loading
Loading