Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh
# Pre-push hook — push protection for secrets (local side of check:secrets).
# Install once per clone: git config core.hooksPath .githooks
# Blocks the push when gitleaks finds a leak.
set -eu
if ! command -v gitleaks >/dev/null 2>&1; then
echo "pre-push: missing tool gitleaks (see .github/workflows/verify.yml pinned install)"
exit 2
fi
echo "pre-push: running gitleaks (history)…"
gitleaks detect --source . --config .gitleaks.toml --verbose
21 changes: 21 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!-- PR title: `<scope>: <what changed>`. Target: `main` only. -->
## What / why

- What changed:
- Why:

## Verification (Delivery+Verification)

Commands actually run + outcomes (include baseline failures separately):

- [ ] `python -m pytest tests/ -v`
- [ ] frontend `npm run lint` / `npm run build` (if touched)
- [ ] `git diff --check`
- [ ] `gitleaks detect --source . --config .gitleaks.toml --verbose`
- [ ] Docs updated in same PR (if behavior changed)

## Trailer (required — Closes/Refs where supported)

<!-- Keep exactly one line. GitHub preserves this on squash-merge. -->

Refs #<n> - <reason> <!-- or: Closes #<n> -->
50 changes: 50 additions & 0 deletions .github/workflows/secrets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# secrets — history-aware secrets scan for `main`.
# Local parity: `poe check:secrets` (gitleaks).
# Triggers: PRs and pushes targeting `main` only, plus nightly and manual.
# No `|| true`, no `continue-on-error`. Does not touch release/ccpe-v1.0.
name: secrets

on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: "17 2 * * *"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: secrets-${{ github.ref }}
cancel-in-progress: true

jobs:
secrets-history:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout (full history for history-aware scan)
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install gitleaks (pinned)
run: |
set -eu
GITLEAKS_VERSION="8.30.1"
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -o /tmp/gitleaks.tar.gz
tar -xzf /tmp/gitleaks.tar.gz -C /tmp
sudo mv /tmp/gitleaks /usr/local/bin/gitleaks
gitleaks version

- name: Gitleaks history scan
run: |
set -eu
gitleaks detect --source . --config .gitleaks.toml --verbose

- name: Gitleaks working-tree scan
run: |
set -eu
gitleaks detect --source . --config .gitleaks.toml --no-git --verbose
131 changes: 131 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# verify — required verification gate for `main`.
# Local parity: `poe verify` / `make verify` run the same standard checks.
# Triggers: PRs and pushes targeting `main` only, plus manual.
# No `|| true`, no `continue-on-error`. Does not touch release/ccpe-v1.0.
# Standard tools only: pytest, git, grep, gitleaks, python stdlib, npm.
name: verify

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: verify-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout (full history for history-aware scan)
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"

- name: Install Python deps
run: |
set -eu
python -m pip install -r requirements.txt

- name: Backend suite (pytest)
run: |
set -eu
python -m pytest tests/ -v

- name: Whitespace hygiene (git diff --check)
run: |
set -eu
git diff --check

- name: Stub hygiene (grep, no placeholders)
run: |
set -eu
# Fail on bare stubs; scoped TODO(ref) passes, binaries skipped.
if git grep -I -n -E -e 'TODO[^\(]|FIXME|XXX|HACK|lorem|NotImplementedError' --and --not -e 'TODO\[' -- . ':!frontend/node_modules' ':!paper' ':!frontend/package-lock.json'; then
echo "stub hygiene FAIL — remove bare stubs above"
exit 1
fi
echo "stub hygiene ok"

- name: Install gitleaks (pinned)
run: |
set -eu
GITLEAKS_VERSION="8.30.1"
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -o /tmp/gitleaks.tar.gz
tar -xzf /tmp/gitleaks.tar.gz -C /tmp
sudo mv /tmp/gitleaks /usr/local/bin/gitleaks
gitleaks version

- name: Gitleaks history scan
run: |
set -eu
gitleaks detect --source . --config .gitleaks.toml --verbose

- name: Gitleaks working-tree scan
run: |
set -eu
gitleaks detect --source . --config .gitleaks.toml --no-git --verbose

- name: Schema compat + restore proof (stdlib only, temp copies)
run: |
set -eu
python3 - <<'PY'
import sqlite3, tempfile
from contextlib import closing
from pathlib import Path
from storage import ensure_schema
with closing(sqlite3.connect(":memory:")) as c:
ensure_schema(c)
tables = [r[0] for r in c.execute("SELECT name FROM sqlite_master WHERE type='table'")]
assert "aws_connections" in tables, "missing aws_connections"
print(f"fresh schema ok: {len(tables)} tables")
with tempfile.TemporaryDirectory() as d:
src = Path("data/cloud_optimizer.db")
dst = Path(d) / "restore.db"
if src.exists():
with closing(sqlite3.connect(f"{src.as_posix()}?mode=ro", uri=True)) as s:
with closing(sqlite3.connect(dst.as_posix())) as t:
s.backup(t)
assert t.execute("PRAGMA integrity_check").fetchone()[0] == "ok"
print("restore round-trip ok")
else:
print("restore skipped: no tracked demo DB")
PY

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: frontend/package-lock.json

- name: Install frontend deps (committed lockfile)
working-directory: frontend
run: |
set -eu
npm ci

- name: Frontend lint
working-directory: frontend
run: |
set -eu
npm run lint

- name: Frontend build
working-directory: frontend
run: |
set -eu
npm run build
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,14 @@ backups/

# Per-user runtime settings, including temporary and corrupt recovery copies
backend_api/runtime_settings.json*

# Paper + support — never on main (paper lives on release/paper branch)
paper/
support_info/
*.Zone.Identifier
# Local runtime artifacts
backend_api/runtime_settings.json
backend_api/runtime_settings.json.corrupt
data/*.backup
data/*.db-shm
data/*.db-wal
32 changes: 32 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Gitleaks config — Smart Cloud Optimizer (main).
# Full-history + working-tree scan runs via `poe check:secrets` / `make check-secrets`
# and .github/workflows/secrets.yml + verify.yml (pinned gitleaks).
# Fail-closed: extends default rules; allowlists cover only synthetic demo
# placeholders and AWS documentation example keys, plus build-artifact paths.
# Real leaks fail the scan. Do not add real-key patterns here.

title = "smart-cloud-optimizer"

[extend]
useDefault = true

[allowlist]
description = "Synthetic demo placeholders and AWS docs example keys — never real credentials"
regexTarget = "match"
regexes = [
'''(?i)SYNTHETIC-001''',
'''aws-SYNTHETIC-001''',
'''SYNTHETIC-001''',
'''AKIAIOSFODNN7EXAMPLE''',
'''wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY''',
]
paths = [
'''frontend/\.next/.*''',
'''\.next/.*''',
'''frontend/node_modules/.*''',
'''frontend/out/.*''',
'''\.pytest_cache/.*''',
'''\.pytest_tmp/.*''',
'''htmlcov/.*''',
'''.*\.pyc$''',
]
8 changes: 8 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# CODEOWNERS — application PRs target `main`; `release/ccpe-v1.0` untouched.
# Fallback first: everything needs the owner when no scoped rule matches.

* @G1DO
/backend_api/ @G1DO
/frontend/ @G1DO
/storage/ @G1DO
/docs/ @G1DO
38 changes: 38 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Contributing

Application changes target **`main`** only. The default `release/ccpe-v1.0`
branch is a separate paper artifact; select `main` explicitly when opening
an application PR. This repo is a single-user localhost demo — see
[threat model](docs/security/threat-model.md) before connecting real data.

## PRs: title, body, trailers

- Title: `<scope>: <what changed>` (e.g. `docs: clarify sync recovery`).
Keep it specific; no `WIP` in the title at merge time.
- Body must state: what changed, why, and verification actually run
(commands + outcomes, including baseline failures). Template:
[.github/pull_request_template.md](.github/pull_request_template.md).
- Every PR body ends with a trailer line. Use `Closes #<n>` when the PR
resolves the issue, `Refs #<n>` when it only relates:
`Closes #12` / `Refs #12`. No issue → `Refs N/A - <reason>`
(e.g. small doc fix). GitHub preserves the PR body on squash-merge,
so the trailer is the audit link. Next 2 PRs must carry trailers;
baseline at time of writing: 0 `Closes`/`Refs` trailers in 112 commits.

## Before opening

- Start with [README setup](README.md#run--docker), then
`README.md` Verify for commands. Verbs: `pyproject.toml [tool.poe.tasks]`, `Makefile`.
- Run the relevant checks and report results in the PR:
`pytest`, frontend `lint`/`build` when touched, `git diff --check`,
`gitleaks detect --source . --config .gitleaks.toml --verbose`.
Same checks run in CI on `main` (see `.github/workflows/`).
- Update affected technical docs in the same PR as code. Docs are minimal:
root `README.md` + `docs/architecture/*.mmd` + `data-model.md` + `threat-model.md` + 1 ADR.
`operations/runbook.md` absent pre-prod.
- Never add credentials, personal data, account identifiers, or runtime
database changes. Keep `.env` and snapshots out of Git. Redact logs
before sharing.

For vulnerability reporting use [SECURITY.md](SECURITY.md) — private route
only, never public issues/PRs.
79 changes: 79 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Makefile — same verbs as [tool.poe.tasks] (short shell steps, real tools).
# No glue scripts; missing tool fails closed (exit 2), never silent pass.
# Prototype note: test + verify are the only required gates for
# prototype-stage changes; the other verbs run on demand or in review.
# Usage: make verify | make test | make check-secrets (or make 'check:secrets').

.PHONY: help dev test migrate build test-contract test\:contract \
check-sast check\:sast check-secrets check\:secrets check-audit check\:audit \
check-hygiene check\:hygiene diagrams verify

help:
@echo "verbs: dev test migrate build test:contract check:sast check:secrets"
@echo " check:audit check:hygiene diagrams verify (prototype: test + verify only)"

dev:
@python3 -c "import uvicorn" 2>/dev/null || { echo "dev: missing tool uvicorn (pip install -r requirements.txt)"; exit 2; }
python3 -m uvicorn backend_api.main:app --reload --host 127.0.0.1 --port 8000

test:
@python3 -c "import pytest" 2>/dev/null || { echo "test: missing tool pytest (pip install -r requirements.txt)"; exit 2; }
python3 -m pytest tests/ -v

migrate:
python3 -c "import sqlite3; from storage import ensure_schema; c=sqlite3.connect(':memory:'); ensure_schema(c); c.close(); print('migrate ok')"

build:
@command -v npm >/dev/null 2>&1 || { echo "build: missing tool npm (install Node 22+)"; exit 2; }
python3 -m compileall -q backend_api cloud_optimizer aws_collector storage data_generation ml_engine optimizer ai_module dashboard
npm --prefix frontend run build

test-contract:
@test -f tests/test_frontend_backend_contract.py || { echo "test:contract omitted: no contract suite in tree"; exit 0; }
@python3 -c "import pytest" 2>/dev/null || { echo "test:contract: missing tool pytest (pip install -r requirements.txt)"; exit 2; }
python3 -m pytest tests/test_frontend_backend_contract.py -v

test\:contract: test-contract

check-sast:
@python3 -c "import bandit" 2>/dev/null || { echo "check:sast: missing tool bandit (pip install bandit)"; exit 2; }
python3 -m bandit -r backend_api storage -q

check\:sast: check-sast

check-secrets:
@command -v gitleaks >/dev/null 2>&1 || { echo "check:secrets: missing tool gitleaks (see .github/workflows/verify.yml pinned install)"; exit 2; }
gitleaks detect --source . --config .gitleaks.toml --verbose
gitleaks detect --source . --config .gitleaks.toml --no-git --verbose

check\:secrets: check-secrets

check-audit:
python3 -m pip check
@if command -v pip-audit >/dev/null 2>&1; then pip-audit; else echo "check:audit note: pip-audit not installed — pip check passed (omitted with reason)"; fi

check\:audit: check-audit

check-hygiene:
@command -v git >/dev/null 2>&1 || { echo "check:hygiene: missing tool git"; exit 2; }
git diff --check
@if git grep -I -n -E -e 'TODO[^(]|FIXME|XXX|HACK|lorem|NotImplementedError' --and --not -e 'TODO\[' -- . ':!frontend/node_modules' ':!paper' ':!frontend/package-lock.json'; then echo "stub hygiene FAIL"; exit 1; fi

check\:hygiene: check-hygiene

diagrams:
@files=$$(find . -name '*.mmd' -not -path './node_modules/*' -not -path './frontend/node_modules/*' -not -path './frontend/.next/*' -not -path './.git/*' -print); \
if [ -z "$$files" ]; then echo "diagrams omitted: no .mmd sources in tree (add docs/<area>/<name>.mmd to enable)"; exit 0; fi; \
command -v mmdc >/dev/null 2>&1 || { echo "diagrams: missing tool mermaid-cli (npm i -g @mermaid-js/mermaid-cli)"; exit 2; }; \
for f in $$files; do mmdc -i "$$f" -o "$${f%.mmd}.svg"; done

verify:
@python3 -c "import pytest" 2>/dev/null || { echo "verify: missing tool pytest (pip install -r requirements.txt)"; exit 2; }
@command -v npm >/dev/null 2>&1 || { echo "verify: missing tool npm (install Node 22+)"; exit 2; }
@command -v git >/dev/null 2>&1 || { echo "verify: missing tool git"; exit 2; }
@command -v gitleaks >/dev/null 2>&1 || { echo "verify: missing tool gitleaks"; exit 2; }
python3 -m pytest tests/ -v
git diff --check
gitleaks detect --source . --config .gitleaks.toml --verbose
npm --prefix frontend run lint
npm --prefix frontend run build
Loading
Loading