From a7728c6266b66b7265c15d8743751305fa873566 Mon Sep 17 00:00:00 2001 From: Max Mayfield Date: Sat, 7 Mar 2026 07:31:16 +0000 Subject: [PATCH] AI SDLC Standards: cross-cutting requirements mono repo - Security: input validation, SQL injection, auth annotations, secrets, CVE checks - Architecture: API contract first, service boundaries, breaking change protocol - DevOps: health checks, structured logging, resource limits, rollback safety - Cost: resource tagging, auto-scaling limits, storage lifecycle - Deterministic compliance checker (.tests/check.sh) - Agent skill for context injection (Cursor, OpenSpec, Claude Code examples) - Demo with intentional violations --- .demo/k8s/deployment.yaml | 14 ++ .../java/com/reltio/demo/UserController.java | 42 ++++++ .../db/migration/V2__update_users.sql | 7 + .tests/check.sh | 133 ++++++++++++++++++ README.md | 33 +++++ architecture/OWNERS | 1 + architecture/requirements.md | 39 +++++ cost/OWNERS | 1 + cost/requirements.md | 32 +++++ devops/OWNERS | 1 + devops/requirements.md | 36 +++++ security/OWNERS | 1 + security/requirements.md | 54 +++++++ skill/SKILL.md | 82 +++++++++++ 14 files changed, 476 insertions(+) create mode 100644 .demo/k8s/deployment.yaml create mode 100644 .demo/src/main/java/com/reltio/demo/UserController.java create mode 100644 .demo/src/main/resources/db/migration/V2__update_users.sql create mode 100755 .tests/check.sh create mode 100644 README.md create mode 100644 architecture/OWNERS create mode 100644 architecture/requirements.md create mode 100644 cost/OWNERS create mode 100644 cost/requirements.md create mode 100644 devops/OWNERS create mode 100644 devops/requirements.md create mode 100644 security/OWNERS create mode 100644 security/requirements.md create mode 100644 skill/SKILL.md diff --git a/.demo/k8s/deployment.yaml b/.demo/k8s/deployment.yaml new file mode 100644 index 0000000..94ed340 --- /dev/null +++ b/.demo/k8s/deployment.yaml @@ -0,0 +1,14 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service +spec: + replicas: 2 + template: + spec: + containers: + - name: user-service + image: reltio/user-service:latest + ports: + - containerPort: 8080 + # OPS-003 VIOLATION: No resource limits defined diff --git a/.demo/src/main/java/com/reltio/demo/UserController.java b/.demo/src/main/java/com/reltio/demo/UserController.java new file mode 100644 index 0000000..8b6f2b9 --- /dev/null +++ b/.demo/src/main/java/com/reltio/demo/UserController.java @@ -0,0 +1,42 @@ +// Sample service with intentional violations for demo purposes +package com.reltio.demo; + +import org.springframework.web.bind.annotation.*; + +// SEC-003 VIOLATION: Missing @ReltioSecured annotation +@RestController +@RequestMapping("/api/users") +public class UserController { + + private final UserRepository repo; + + public UserController(UserRepository repo) { + this.repo = repo; + } + + // SEC-001 VIOLATION: Raw request parameter access + // SEC-002 VIOLATION: SQL string concatenation + @GetMapping("/search") + public List search(HttpServletRequest request) { + String name = request.getParameter("name"); + return repo.query("SELECT * FROM users WHERE name = '" + name + "'"); + } + + // COMPLIANT: Validated input, parameterized query, auth annotation + @ReltioSecured(resource = "users", privilege = "READ") + @GetMapping("/{id}") + public User getById(@PathVariable @Valid Long id) { + return repo.findById(id); + } + + // SEC-004 VIOLATION: Hardcoded secret + private static final String API_SECRET = "sk-reltio-prod-a8f3b2c1d4e5f6789"; + + // OPS-002 VIOLATION: Raw stdout logging + @PostMapping + @ReltioSecured(resource = "users", privilege = "WRITE") + public User create(@RequestBody @Valid CreateUserRequest req) { + System.out.println("Creating user: " + req.getName()); + return repo.save(req.toUser()); + } +} diff --git a/.demo/src/main/resources/db/migration/V2__update_users.sql b/.demo/src/main/resources/db/migration/V2__update_users.sql new file mode 100644 index 0000000..f2ec55f --- /dev/null +++ b/.demo/src/main/resources/db/migration/V2__update_users.sql @@ -0,0 +1,7 @@ +-- OPS-004 VIOLATION: Destructive migration +ALTER TABLE users DROP COLUMN legacy_name; +ALTER TABLE users RENAME COLUMN full_name TO display_name; + +-- COMPLIANT: Additive migration +ALTER TABLE users ADD COLUMN email VARCHAR(255); +CREATE INDEX idx_users_email ON users(email); diff --git a/.tests/check.sh b/.tests/check.sh new file mode 100755 index 0000000..30bc95e --- /dev/null +++ b/.tests/check.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +# Deterministic compliance checker +# Runs against a diff or directory and checks all requirement categories +# Usage: ./check.sh [--diff ] +set -euo pipefail + +REPO="${1:-.}" +DIFF_MODE=false +BASE_BRANCH="" +VIOLATIONS=0 +WARNINGS=0 + +if [[ "${2:-}" == "--diff" ]]; then + DIFF_MODE=true + BASE_BRANCH="${3:-main}" +fi + +RED='\033[0;31m' +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +NC='\033[0m' + +pass() { echo -e " ${GREEN}✓${NC} $1"; } +warn() { echo -e " ${YELLOW}⚠${NC} $1"; WARNINGS=$((WARNINGS + 1)); } +fail() { echo -e " ${RED}✗${NC} $1"; VIOLATIONS=$((VIOLATIONS + 1)); } + +get_changed_files() { + if $DIFF_MODE; then + git -C "$REPO" diff --name-only "$BASE_BRANCH" -- '*.java' '*.go' '*.js' '*.ts' '*.py' '*.kt' '*.sql' '*.yaml' '*.yml' '*.tf' '*.json' 2>/dev/null || true + else + find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.js' -o -name '*.ts' -o -name '*.py' -o -name '*.kt' \) ! -path '*/test/*' ! -path '*/node_modules/*' ! -path '*/.git/*' 2>/dev/null + fi +} + +FILES=$(get_changed_files) + +echo "═══════════════════════════════════════" +echo " AI SDLC Standards Compliance Check" +echo "═══════════════════════════════════════" +echo "" + +# ── SEC-001: Input Validation ── +echo "▸ SEC-001: Input Validation" +RAW_ACCESS=$(echo "$FILES" | xargs grep -ln 'request\.getParameter\|req\.body\b\|request\.body\b' 2>/dev/null | grep -v test || true) +if [[ -n "$RAW_ACCESS" ]]; then + for f in $RAW_ACCESS; do fail "Raw request access: $f"; done +else + pass "No raw request body access found" +fi + +# ── SEC-002: No Raw SQL ── +echo "▸ SEC-002: No Raw SQL" +SQL_CONCAT=$(echo "$FILES" | xargs grep -Pln '(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE).*["\x27]\s*\+\s*|String\.format.*?(SELECT|INSERT|UPDATE|DELETE)' 2>/dev/null | grep -v test || true) +if [[ -n "$SQL_CONCAT" ]]; then + for f in $SQL_CONCAT; do fail "SQL concatenation: $f"; done +else + pass "No SQL string concatenation found" +fi + +# ── SEC-003: Auth Annotations ── +echo "▸ SEC-003: Authentication Annotations" +ENDPOINTS=$(echo "$FILES" | xargs grep -ln '@RequestMapping\|@GetMapping\|@PostMapping\|@PutMapping\|@DeleteMapping' 2>/dev/null || true) +for f in $ENDPOINTS; do + MISSING=$(grep -B5 '@\(Request\|Get\|Post\|Put\|Delete\)Mapping' "$f" | grep -c '@ReltioSecured\|@PublicEndpoint\|@PreAuthorize' || true) + TOTAL=$(grep -c '@\(Request\|Get\|Post\|Put\|Delete\)Mapping' "$f" || true) + if [[ "$MISSING" -lt "$TOTAL" ]]; then + warn "Endpoint without explicit auth: $f ($MISSING/$TOTAL annotated)" + fi +done +if [[ -z "$ENDPOINTS" ]]; then pass "No endpoint files in scope"; fi + +# ── SEC-004: Secrets in Code ── +echo "▸ SEC-004: Secrets in Code" +SECRETS=$(echo "$FILES" | xargs grep -Piln '(password|secret|api_key|apikey|token)\s*=\s*["\x27][^"\x27]{8,}' 2>/dev/null | grep -v test | grep -v '\.env\.example' || true) +if [[ -n "$SECRETS" ]]; then + for f in $SECRETS; do fail "Possible hardcoded secret: $f"; done +else + pass "No hardcoded secrets detected" +fi + +# ── OPS-002: Structured Logging ── +echo "▸ OPS-002: Structured Logging" +RAW_LOG=$(echo "$FILES" | xargs grep -ln 'System\.out\.print\|System\.err\.print' 2>/dev/null | grep -v test || true) +if [[ -n "$RAW_LOG" ]]; then + for f in $RAW_LOG; do warn "Raw stdout/stderr logging: $f"; done +else + pass "No raw stdout/stderr logging" +fi + +# ── OPS-003: Resource Limits ── +echo "▸ OPS-003: Resource Limits" +K8S_DEPLOYS=$(find "$REPO" -name '*.yaml' -o -name '*.yml' | xargs grep -l 'kind: Deployment' 2>/dev/null || true) +for f in $K8S_DEPLOYS; do + if ! grep -q 'resources:' "$f"; then + fail "K8s deployment missing resource limits: $f" + else + pass "Resource limits present: $f" + fi +done + +# ── OPS-004: Rollback Safety ── +echo "▸ OPS-004: Rollback Safety" +MIGRATIONS=$(find "$REPO" -path '*/migration*' -name '*.sql' 2>/dev/null || true) +for f in $MIGRATIONS; do + DANGEROUS=$(grep -Pic 'DROP\s+COLUMN|RENAME\s+COLUMN|ALTER\s+.*TYPE' "$f" || true) + if [[ "$DANGEROUS" -gt 0 ]]; then + fail "Destructive migration: $f (DROP/RENAME/ALTER TYPE)" + fi +done +if [[ -z "$MIGRATIONS" ]]; then pass "No migration files in scope"; fi + +# ── COST-001: Resource Tagging ── +echo "▸ COST-001: Resource Tagging" +TF_FILES=$(find "$REPO" -name '*.tf' 2>/dev/null || true) +for f in $TF_FILES; do + if grep -q 'resource\s' "$f" && ! grep -q 'tags' "$f"; then + warn "Terraform resource without tags: $f" + fi +done +if [[ -z "$TF_FILES" ]]; then pass "No Terraform files in scope"; fi + +echo "" +echo "═══════════════════════════════════════" +if [[ $VIOLATIONS -gt 0 ]]; then + echo -e " Result: ${RED}FAIL${NC} — $VIOLATIONS violation(s), $WARNINGS warning(s)" + exit 1 +elif [[ $WARNINGS -gt 0 ]]; then + echo -e " Result: ${YELLOW}WARN${NC} — $WARNINGS warning(s)" + exit 0 +else + echo -e " Result: ${GREEN}PASS${NC} — all checks passed" + exit 0 +fi diff --git a/README.md b/README.md new file mode 100644 index 0000000..8bbfcee --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# AI SDLC Standards + +Cross-cutting non-functional requirements for AI-assisted software development. + +## Structure + +``` +security/ — InfoSec requirements (owned by Security team) +architecture/ — Software architecture standards (owned by Architecture team) +devops/ — CI/CD and deployment requirements (owned by DevOps team) +cost/ — Cost attribution and resource tagging (owned by FinOps team) +.tests/ — Deterministic compliance checks +skill/ — Agent skill for context injection +``` + +## How It Works + +1. Each folder contains **testable requirements** in markdown — specific rules an AI agent (or human) must follow. +2. The **skill** teaches your AI agent where to find these requirements and when to apply them. +3. **Deterministic tests** in `.tests/` validate compliance at CI time — fast, free, no LLM needed. +4. Each folder has an `OWNERS` file. That team maintains and evolves their requirements. + +## Philosophy + +- **Standardize the input, not the tool.** Use OpenSpec, BMad, Cursor rules, or anything else. These requirements feed into whatever workflow you already have. +- **Progressive enforcement.** Start informational. Graduate to blocking as requirements mature. +- **Concrete over aspirational.** Every requirement must be testable. If you can't write a check for it, it's not a requirement — it's a wish. + +## Getting Started + +Plug the skill into your AI agent's configuration. It will pull the right requirements at the right phase of development. + +See `skill/SKILL.md` for integration instructions. diff --git a/architecture/OWNERS b/architecture/OWNERS new file mode 100644 index 0000000..3cbbdb9 --- /dev/null +++ b/architecture/OWNERS @@ -0,0 +1 @@ +@architecture-team diff --git a/architecture/requirements.md b/architecture/requirements.md new file mode 100644 index 0000000..9e42d48 --- /dev/null +++ b/architecture/requirements.md @@ -0,0 +1,39 @@ +# Architecture Requirements + +Phase: design +Enforcement: informational (graduating to blocking Q3 2026) + +## ARCH-001: API Contract First + +All new or modified REST APIs MUST have an updated OpenAPI/Swagger spec BEFORE implementation begins. + +**Rule:** If a PR adds or changes an endpoint, the corresponding `openapi.yaml` or Swagger annotation must be updated in the same PR. + +**Test:** Diff check — if files in `src/**/controller/**` or `src/**/api/**` changed, verify `**/openapi*.yaml` or `**/swagger*.yaml` also changed. + +## ARCH-002: Service Boundary Respect + +Changes to a service MUST NOT directly import or reference internal classes from another service's module. + +**Rule:** Cross-service communication happens through APIs, events, or SDK interfaces only. No direct classpath coupling between service modules. + +**Test:** Import scan — flag imports crossing module boundaries (e.g., `import com.reltio.server.internal.*` from SDK module). + +## ARCH-003: Breaking Change Protocol + +Any breaking API change (removed field, changed type, renamed endpoint) MUST include: +1. A deprecation annotation on the old path +2. A migration note in the PR description +3. A minimum 30-day dual-support window + +**Rule:** Breaking changes without deprecation path are rejected. + +**Test:** Detect removed/renamed public API methods or fields in diff. Flag if no `@Deprecated` annotation present. + +## ARCH-004: Multi-Repository Coordination + +For changes spanning multiple repositories (e.g., SDK + Server), the design spec MUST list all affected repos and the order of deployment. + +**Rule:** Multi-repo PRs must reference a shared Jira ticket and declare deployment sequence. + +**Test:** If PR description references multiple repos, verify Jira link present. diff --git a/cost/OWNERS b/cost/OWNERS new file mode 100644 index 0000000..616cd00 --- /dev/null +++ b/cost/OWNERS @@ -0,0 +1 @@ +@finops-team diff --git a/cost/requirements.md b/cost/requirements.md new file mode 100644 index 0000000..5ee5ed5 --- /dev/null +++ b/cost/requirements.md @@ -0,0 +1,32 @@ +# Cost & Tagging Requirements + +Phase: deployment +Enforcement: informational + +## COST-001: Resource Tagging + +All cloud resources (AWS, GCP, Azure) MUST include the following tags: +- `team` — owning team name +- `service` — service identifier +- `environment` — dev/staging/prod +- `cost-center` — finance cost center code + +**Rule:** Infrastructure-as-code (Terraform, CloudFormation, Pulumi) must include these tags on every resource that supports tagging. + +**Test:** Parse IaC files, verify tag block contains all four required keys. + +## COST-002: No Open-Ended Auto-Scaling + +Auto-scaling configurations MUST define a `maxReplicas` / `maxCapacity` ceiling. + +**Rule:** Unbounded scaling is a cost incident waiting to happen. Every autoscaler must have an explicit maximum. + +**Test:** Parse HPA/scaling configs, verify `maxReplicas` is set and is not unreasonably high (>50 requires justification). + +## COST-003: Storage Lifecycle + +All S3 buckets / GCS buckets / Blob containers MUST have a lifecycle policy defined. + +**Rule:** No indefinite storage retention. Every bucket must transition to cheaper tiers or expire objects after a defined period. + +**Test:** Check IaC for lifecycle configuration on storage resources. diff --git a/devops/OWNERS b/devops/OWNERS new file mode 100644 index 0000000..fe855c4 --- /dev/null +++ b/devops/OWNERS @@ -0,0 +1 @@ +@devops-team diff --git a/devops/requirements.md b/devops/requirements.md new file mode 100644 index 0000000..55a0f87 --- /dev/null +++ b/devops/requirements.md @@ -0,0 +1,36 @@ +# DevOps Requirements + +Phase: deployment +Enforcement: informational (graduating to blocking Q3 2026) + +## OPS-001: Health Check Endpoint + +Every deployable service MUST expose a `/health` or `/actuator/health` endpoint that returns 200 when healthy. + +**Rule:** New services must include a health check. Existing services adding deployment config must verify health endpoint exists. + +**Test:** Check that service has a health endpoint registered (grep for health route registration). + +## OPS-002: Structured Logging + +All log statements MUST use structured logging (JSON format) with at minimum: timestamp, level, service name, correlation ID. + +**Rule:** No `System.out.println`, `console.log` for production logging. Use the logging framework with structured output. + +**Test:** Grep for `System.out.print`, `System.err.print`, raw `console.log` in non-test source files. + +## OPS-003: Resource Limits + +All Kubernetes deployment manifests MUST specify CPU and memory requests and limits. + +**Rule:** No unbounded resource consumption. Every container spec must have `resources.requests` and `resources.limits`. + +**Test:** Parse YAML deployment files, verify `resources` block present with both `requests` and `limits`. + +## OPS-004: Rollback Safety + +Database migrations MUST be backward-compatible with the previous service version (N-1 compatibility). + +**Rule:** No column renames or drops without a multi-step migration. Additive changes only in a single release. + +**Test:** Scan migration SQL for `DROP COLUMN`, `RENAME COLUMN`, `ALTER TYPE` — flag for manual review. diff --git a/security/OWNERS b/security/OWNERS new file mode 100644 index 0000000..ef1d270 --- /dev/null +++ b/security/OWNERS @@ -0,0 +1 @@ +@security-team diff --git a/security/requirements.md b/security/requirements.md new file mode 100644 index 0000000..6bddaae --- /dev/null +++ b/security/requirements.md @@ -0,0 +1,54 @@ +# Security Requirements + +Phase: implementation +Enforcement: informational (graduating to blocking Q3 2026) + +## SEC-001: Input Validation + +All external input (API request bodies, query parameters, headers, file uploads) MUST be validated through a schema validator before processing. + +**Rule:** No raw request body access in business logic. All endpoints must define and validate against a schema (JSON Schema, protobuf, or framework-equivalent). + +**Test:** Grep for direct `request.body` / `req.body` / `getParameter()` usage outside of controller/validation layer. + +``` +# Bad +String name = request.getParameter("name"); +db.query("SELECT * FROM users WHERE name = '" + name + "'"); + +# Good +ValidatedInput input = validator.validate(request, CreateUserSchema.class); +userService.create(input); +``` + +## SEC-002: No Raw SQL + +All database queries MUST use parameterized queries or an ORM. No string concatenation in SQL statements. + +**Rule:** Zero tolerance for SQL string concatenation with user-controlled values. + +**Test:** Regex scan for SQL keywords adjacent to string concatenation operators (`+`, `concat`, `format`, `f"`, template literals). + +## SEC-003: Authentication Annotations + +All new REST endpoints MUST have an explicit auth annotation. No endpoint may be implicitly public. + +**Rule:** Every `@RequestMapping`, `@GetMapping`, `@PostMapping` (or equivalent) must be accompanied by `@ReltioSecured` or `@PublicEndpoint`. Missing annotation = violation. + +**Test:** AST/regex check that every endpoint method has an auth annotation. + +## SEC-004: Secrets in Code + +No hardcoded secrets, tokens, passwords, or API keys in source code. + +**Rule:** All secrets must come from environment variables, vault, or config service. String literals matching secret patterns are violations. + +**Test:** Regex scan for patterns: API keys, JWT tokens, passwords in string literals, base64-encoded credentials. + +## SEC-005: Dependency Vulnerability + +No new dependencies with known critical/high CVEs. + +**Rule:** Any new dependency added to `pom.xml`, `package.json`, `go.mod`, or equivalent must pass a vulnerability scan. + +**Test:** Run `npm audit` / `mvn dependency-check:check` / `govulncheck` on changed dependency files. diff --git a/skill/SKILL.md b/skill/SKILL.md new file mode 100644 index 0000000..ef5dd6c --- /dev/null +++ b/skill/SKILL.md @@ -0,0 +1,82 @@ +--- +name: sdlc-standards +description: Inject cross-cutting non-functional requirements into AI agent context during software development. Use when starting work on a Jira story, designing a feature, implementing code, or preparing for deployment. Pulls security, architecture, DevOps, and cost requirements from the standards mono repo at the appropriate development phase. +--- + +# SDLC Standards Skill + +Provides non-functional requirements to your AI agent based on the current development phase. + +## Setup + +Clone the standards repo into your workspace: +```bash +git clone .standards +``` + +Or add as a git submodule: +```bash +git submodule add .standards +``` + +## When to Load Requirements + +| Phase | Load these | Why | +|-------|-----------|-----| +| Design / Exploration | `architecture/requirements.md` | API contracts, service boundaries, breaking change protocol | +| Implementation | `security/requirements.md` | Input validation, auth, secrets, SQL safety | +| Deployment config | `devops/requirements.md`, `cost/requirements.md` | Health checks, logging, resource limits, tagging | + +## Usage + +When starting a new story, tell your agent: + +> Before implementing, read the requirements from `.standards/` that apply to this phase. For design work, read `.standards/architecture/requirements.md`. For implementation, read `.standards/security/requirements.md`. For deployment changes, read `.standards/devops/requirements.md` and `.standards/cost/requirements.md`. + +Or configure your agent rules to auto-load: + +### Cursor (.cursor/rules) +``` +When working on this project, check .standards/ for non-functional requirements. +Load architecture requirements during design. Load security requirements during implementation. +Load devops and cost requirements when modifying deployment configs. +``` + +### OpenSpec (openspec.config.yaml) +```yaml +context: + - path: .standards/architecture/requirements.md + phase: propose + - path: .standards/security/requirements.md + phase: apply + - path: .standards/devops/requirements.md + phase: apply +``` + +### Claude Code (CLAUDE.md) +```markdown +## Standards +Before implementing, read applicable requirements from `.standards/`: +- Design: `.standards/architecture/requirements.md` +- Code: `.standards/security/requirements.md` +- Deploy: `.standards/devops/requirements.md` and `.standards/cost/requirements.md` +``` + +## CI Integration + +Run the compliance checker in your pipeline: + +```bash +bash .standards/.tests/check.sh . --diff main +``` + +Returns exit code 0 (pass/warn) or 1 (violations). Start with `|| true` to make it informational, remove when ready to enforce. + +```groovy +// Jenkinsfile example +stage('Standards Check') { + steps { + sh 'bash .standards/.tests/check.sh . --diff main || true' + } +} +```