Elevate requirements to organizational/architectural policy

- Security: no IAM in service repos, no custom auth, no direct external calls
- Architecture: no cross-cloud SDKs, no cross-service DB access, no hardcoded tenant/env config
- DevOps: Foxtrot-compatible Helm (no custom ingress), no infra provisioning in service repos, no pinned infra versions
- Cost: resource tagging, no unbounded allocation, no per-tenant infra
- Updated checker and demo to match
- These are NOT static code analysis — they catch organizational policy violations that SonarQube/Checkstyle miss
This commit is contained in:
Max Mayfield
2026-03-07 07:41:27 +00:00
parent a7728c6266
commit e323c45cb0
10 changed files with 265 additions and 198 deletions

6
.demo/dependencies.txt Normal file
View File

@@ -0,0 +1,6 @@
# ARCH-001 VIOLATION: Direct cloud SDK dependencies
com.amazonaws:aws-java-sdk-s3:1.12.400
com.google.cloud:google-cloud-storage:2.20.0
# SEC-002 VIOLATION: JWT library for custom auth
io.jsonwebtoken:jjwt:0.9.1

28
.demo/infra/main.tf Normal file
View File

@@ -0,0 +1,28 @@
# SEC-001 VIOLATION: IAM resources in a service repo
resource "aws_iam_role" "service_role" {
name = "user-service-role"
assume_role_policy = data.aws_iam_policy_document.assume.json
}
resource "aws_iam_policy" "s3_access" {
name = "user-service-s3-access"
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["s3:GetObject", "s3:PutObject"]
Resource = "arn:aws:s3:::reltio-prod-data/*"
}]
})
}
# OPS-002 VIOLATION: Infrastructure provisioning in service repo
resource "aws_sqs_queue" "user_events" {
name = "user-events-queue"
}
resource "aws_dynamodb_table" "user_cache" {
name = "user-cache"
billing_mode = "PAY_PER_REQUEST"
hash_key = "userId"
}

View File

@@ -11,4 +11,33 @@ spec:
image: reltio/user-service:latest
ports:
- containerPort: 8080
# OPS-003 VIOLATION: No resource limits defined
---
# OPS-001 VIOLATION: Custom ingress instead of Foxtrot routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: user-service-ingress
spec:
rules:
- host: users.reltio.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: user-service
port:
number: 8080
---
# OPS-004 VIOLATION: Pinned infrastructure version
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-db
spec:
template:
spec:
containers:
- name: postgres
image: postgres:14.2

View File

@@ -1,42 +1,48 @@
// Sample service with intentional violations for demo purposes
// Demo service with intentional violations at the organizational/architectural level
package com.reltio.demo;
import com.amazonaws.services.s3.AmazonS3; // ARCH-001 VIOLATION: Direct AWS SDK import
import com.google.cloud.storage.Storage; // ARCH-001 VIOLATION: Direct GCP SDK import
import io.jsonwebtoken.Jwts; // SEC-002 VIOLATION: Custom JWT handling
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
// SEC-003 VIOLATION: Missing @ReltioSecured annotation
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserRepository repo;
private final AmazonS3 s3Client;
private final RestTemplate restTemplate;
public UserController(UserRepository repo) {
this.repo = repo;
// ARCH-003 VIOLATION: Hardcoded environment URL
private static final String ANALYTICS_URL = "https://prod.reltio.com/analytics/v1";
// ARCH-003 VIOLATION: Hardcoded tenant logic
public Object getTenantConfig(String tenantId) {
if (tenantId.equals("acme-corp")) {
return Map.of("maxEntities", 1000000);
}
return Map.of("maxEntities", 100000);
}
// SEC-001 VIOLATION: Raw request parameter access
// SEC-002 VIOLATION: SQL string concatenation
@GetMapping("/search")
public List<User> search(HttpServletRequest request) {
String name = request.getParameter("name");
return repo.query("SELECT * FROM users WHERE name = '" + name + "'");
// SEC-003 VIOLATION: Direct external HTTP call
public void notifyPartner(String event) {
restTemplate.postForObject("https://api.partner-system.com/webhook", event, String.class);
}
// 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-002 VIOLATION: Custom auth endpoint
@PostMapping("/login")
public String login(@RequestBody LoginRequest req) {
// Custom JWT generation instead of using platform auth
return Jwts.builder().setSubject(req.getUsername()).compact();
}
// 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());
// ARCH-002 VIOLATION: Cross-service database query
public List<Order> getUserOrders(Long userId) {
// Directly querying the orders service's schema
return jdbcTemplate.query(
"SELECT * FROM orders_service.orders WHERE user_id = ?",
new Object[]{userId}, orderRowMapper);
}
}

View File

@@ -1,7 +0,0 @@
-- 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);

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Deterministic compliance checker
# Runs against a diff or directory and checks all requirement categories
# AI SDLC Standards — Deterministic Compliance Checker
# Checks organizational and architectural policy, NOT static code analysis.
# Usage: ./check.sh <path-to-repo> [--diff <base-branch>]
set -euo pipefail
@@ -18,106 +18,136 @@ fi
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
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
# ── SECURITY ──
echo -e "${CYAN}Security${NC}"
# SEC-001: No IAM Resources in Service Repos
echo "▸ SEC-001: No IAM in Service Repos"
IAM_FILES=$(find "$REPO" -name '*.tf' -exec grep -l 'aws_iam_\|google_project_iam\|azurerm_role' {} \; 2>/dev/null || true)
if [[ -n "$IAM_FILES" ]]; then
for f in $IAM_FILES; do fail "IAM resources in service repo: $f"; done
else
pass "No raw request body access found"
pass "No IAM resource definitions 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"
# SEC-002: No Custom Auth / JWT Handling
echo "▸ SEC-002: No Custom Auth Mechanisms"
CUSTOM_AUTH=$(find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.ts' -o -name '*.py' \) ! -path '*/test/*' -exec grep -l 'io\.jsonwebtoken\|jwt\.decode\|jwt\.sign\|JWTVerifier\|PyJWT' {} \; 2>/dev/null || true)
LOGIN_ENDPOINTS=$(find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.ts' -o -name '*.py' \) ! -path '*/test/*' -exec grep -l '"/login"\|"/authenticate"\|"/signin"' {} \; 2>/dev/null || true)
if [[ -n "$CUSTOM_AUTH" ]]; then
for f in $CUSTOM_AUTH; do fail "Custom JWT/auth library: $f"; done
fi
if [[ -n "$LOGIN_ENDPOINTS" ]]; then
for f in $LOGIN_ENDPOINTS; do fail "Custom login endpoint: $f"; done
fi
if [[ -z "$CUSTOM_AUTH" && -z "$LOGIN_ENDPOINTS" ]]; then
pass "No custom auth mechanisms 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
# SEC-003: No Direct External Network Calls
echo "▸ SEC-003: No Direct External Calls"
EXT_CALLS=$(find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.ts' -o -name '*.py' \) ! -path '*/test/*' -exec grep -Pn 'https?://(?!localhost|127\.0\.0\.1|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)(?!.*reltio\.(com|net|io))' {} \; 2>/dev/null | grep -v '^\s*//' | head -5 || true)
if [[ -n "$EXT_CALLS" ]]; then
echo "$EXT_CALLS" | while read -r line; do fail "Direct external call: $line"; done
else
pass "No hardcoded secrets detected"
pass "No direct external network calls"
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
echo ""
# ── ARCHITECTURE ──
echo -e "${CYAN}Architecture${NC}"
# ARCH-001: No Cross-Cloud Dependencies
echo "▸ ARCH-001: No Cross-Cloud SDK Imports"
CLOUD_IMPORTS=$(find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.ts' -o -name '*.py' \) ! -path '*/test/*' ! -path '*/platform/*' -exec grep -ln 'com\.amazonaws\.\|com\.google\.cloud\.\|com\.azure\.\|@aws-sdk\|@google-cloud\|@azure' {} \; 2>/dev/null || true)
if [[ -n "$CLOUD_IMPORTS" ]]; then
for f in $CLOUD_IMPORTS; do fail "Direct cloud SDK import: $f"; done
else
pass "No raw stdout/stderr logging"
pass "No direct cloud SDK imports"
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
# ARCH-002: No Cross-Service Database Access
echo "▸ ARCH-002: No Cross-Service DB Access"
CROSS_DB=$(find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.ts' -o -name '*.py' -o -name '*.sql' \) ! -path '*/test/*' -exec grep -ln '[a-z_]*_service\.\|cross_schema\|foreign_schema' {} \; 2>/dev/null || true)
if [[ -n "$CROSS_DB" ]]; then
for f in $CROSS_DB; do warn "Possible cross-service DB access: $f"; done
else
pass "No cross-service database access detected"
fi
# ── 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
# ARCH-003: No Hardcoded Tenant/Environment Config
echo "▸ ARCH-003: No Hardcoded Tenant/Environment Config"
HARDCODED=$(find "$REPO" -type f \( -name '*.java' -o -name '*.go' -o -name '*.ts' -o -name '*.py' \) ! -path '*/test/*' -exec grep -Pln 'https?://(prod|staging|dev)\.\w+\.(com|net|io)|\.equals\("(acme|tenant|customer)-' {} \; 2>/dev/null || true)
if [[ -n "$HARDCODED" ]]; then
for f in $HARDCODED; do fail "Hardcoded tenant/environment config: $f"; done
else
pass "No hardcoded tenant/environment configuration"
fi
# ── COST-001: Resource Tagging ──
echo ""
# ── DEVOPS ──
echo -e "${CYAN}DevOps${NC}"
# OPS-001: Foxtrot-Compatible Helm Chart (no custom ingress)
echo "▸ OPS-001: No Custom Ingress (Foxtrot Routing)"
CUSTOM_INGRESS=$(find "$REPO" -name '*.yaml' -o -name '*.yml' | xargs grep -l 'kind: Ingress' 2>/dev/null || true)
if [[ -n "$CUSTOM_INGRESS" ]]; then
for f in $CUSTOM_INGRESS; do fail "Custom Ingress resource (Foxtrot manages routing): $f"; done
else
pass "No custom Ingress definitions"
fi
# OPS-002: No Infrastructure Provisioning in Service Repos
echo "▸ OPS-002: No Infrastructure in Service Repos"
INFRA_FILES=$(find "$REPO" -name '*.tf' -o -name 'Pulumi.*' | grep -v '.terraform' 2>/dev/null || true)
CFN_FILES=$(find "$REPO" -name '*.template.yaml' -o -name '*.template.json' | xargs grep -l 'AWSTemplateFormatVersion\|AWS::' 2>/dev/null || true)
if [[ -n "$INFRA_FILES" ]]; then
for f in $INFRA_FILES; do fail "Infrastructure-as-code in service repo: $f"; done
fi
if [[ -n "$CFN_FILES" ]]; then
for f in $CFN_FILES; do fail "CloudFormation template in service repo: $f"; done
fi
if [[ -z "$INFRA_FILES" && -z "$CFN_FILES" ]]; then
pass "No infrastructure provisioning files"
fi
# OPS-004: No Pinned Infrastructure Versions
echo "▸ OPS-004: No Pinned Infrastructure Versions"
PINNED=$(find "$REPO" -name '*.yaml' -o -name '*.yml' | xargs grep -Pn 'image:\s*(postgres|mysql|redis|mongo|elasticsearch|kafka|rabbitmq|zookeeper):\d' 2>/dev/null || true)
if [[ -n "$PINNED" ]]; then
echo "$PINNED" | while read -r line; do fail "Pinned infra version: $line"; done
else
pass "No pinned infrastructure image versions"
fi
echo ""
# ── COST ──
echo -e "${CYAN}Cost${NC}"
# 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
TF_RESOURCES=$(find "$REPO" -name '*.tf' -exec grep -l 'resource\s' {} \; 2>/dev/null || true)
for f in $TF_RESOURCES; do
if ! 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
if [[ -z "$TF_RESOURCES" ]]; then pass "No Terraform resources in scope"; fi
echo ""
echo "═══════════════════════════════════════"

View File

@@ -1,39 +1,36 @@
# Architecture Requirements
Phase: design
Enforcement: informational (graduating to blocking Q3 2026)
Enforcement: informational
## ARCH-001: API Contract First
## ARCH-001: No Cross-Cloud Dependencies
All new or modified REST APIs MUST have an updated OpenAPI/Swagger spec BEFORE implementation begins.
Services MUST NOT import cloud-provider-specific SDKs directly. All cloud interactions go through the platform abstraction layer.
**Rule:** If a PR adds or changes an endpoint, the corresponding `openapi.yaml` or Swagger annotation must be updated in the same PR.
**Rule:** No direct imports of `com.amazonaws.*`, `com.google.cloud.*`, or `com.azure.*` in service code. Use the platform's cloud-agnostic interfaces. If your service runs on AWS today, it must be deployable to GCP tomorrow without code changes.
**Test:** Diff check — if files in `src/**/controller/**` or `src/**/api/**` changed, verify `**/openapi*.yaml` or `**/swagger*.yaml` also changed.
**Test:** Scan imports for cloud-provider SDK packages outside the platform abstraction layer.
## ARCH-002: Service Boundary Respect
## ARCH-002: No Cross-Service Database Access
Changes to a service MUST NOT directly import or reference internal classes from another service's module.
Services MUST NOT directly query another service's database. All cross-service data access goes through APIs or events.
**Rule:** Cross-service communication happens through APIs, events, or SDK interfaces only. No direct classpath coupling between service modules.
**Rule:** Each service owns its schema. No shared database connections, no cross-schema joins, no direct table access outside your service boundary. If you need data from another service, call its API or consume its events.
**Test:** Import scan — flag imports crossing module boundaries (e.g., `import com.reltio.server.internal.*` from SDK module).
**Test:** Scan datasource configs and SQL for references to schemas/tables outside the service's declared ownership.
## ARCH-003: Breaking Change Protocol
## ARCH-003: No Hardcoded Tenant or Environment Configuration
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
Services MUST NOT contain hardcoded tenant IDs, environment URLs, or deployment-specific configuration in source code.
**Rule:** Breaking changes without deprecation path are rejected.
**Rule:** All tenant and environment config comes from the platform config plane (environment variables, config service, or Helm values). No `if (tenant == "acme")` logic. No `https://prod.reltio.com` literals.
**Test:** Detect removed/renamed public API methods or fields in diff. Flag if no `@Deprecated` annotation present.
**Test:** Scan for URL literals matching known environment patterns, hardcoded tenant identifiers, environment-switching conditionals.
## ARCH-004: Multi-Repository Coordination
## ARCH-004: API Contract Versioning
For changes spanning multiple repositories (e.g., SDK + Server), the design spec MUST list all affected repos and the order of deployment.
All public API changes MUST maintain backward compatibility or follow the deprecation protocol (minimum 30-day dual-support window).
**Rule:** Multi-repo PRs must reference a shared Jira ticket and declare deployment sequence.
**Rule:** No removed fields, renamed endpoints, or changed response shapes without a versioned migration path. Breaking changes require a new API version.
**Test:** If PR description references multiple repos, verify Jira link present.
**Test:** Diff OpenAPI specs between branches. Flag removed paths, removed required fields, or changed response types.

View File

@@ -3,30 +3,26 @@
Phase: deployment
Enforcement: informational
## COST-001: Resource Tagging
## COST-001: Standard 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
All cloud resources MUST include the organization's standard tag set for cost attribution.
**Rule:** Infrastructure-as-code (Terraform, CloudFormation, Pulumi) must include these tags on every resource that supports tagging.
**Rule:** Every resource that supports tagging must include: `team`, `service`, `environment`, `cost-center`. These are used for chargeback and cost allocation dashboards.
**Test:** Parse IaC files, verify tag block contains all four required keys.
## COST-002: No Open-Ended Auto-Scaling
## COST-002: No Unbounded Resource Allocation
Auto-scaling configurations MUST define a `maxReplicas` / `maxCapacity` ceiling.
Service configurations MUST define resource ceilings. No open-ended scaling or storage without limits.
**Rule:** Unbounded scaling is a cost incident waiting to happen. Every autoscaler must have an explicit maximum.
**Rule:** Auto-scaling must have explicit maximums. Storage must have lifecycle policies. Compute must have resource limits. "Unlimited" is not a valid configuration.
**Test:** Parse HPA/scaling configs, verify `maxReplicas` is set and is not unreasonably high (>50 requires justification).
**Test:** Check scaling configs for `maxReplicas`/`maxCapacity`. Check storage for lifecycle rules.
## COST-003: Storage Lifecycle
## COST-003: No Dedicated Infrastructure Per Tenant
All S3 buckets / GCS buckets / Blob containers MUST have a lifecycle policy defined.
Services MUST NOT provision tenant-specific infrastructure (dedicated databases, queues, or compute per customer).
**Rule:** No indefinite storage retention. Every bucket must transition to cheaper tiers or expire objects after a defined period.
**Rule:** Multi-tenancy is handled at the application layer, not the infrastructure layer. Tenant isolation through data partitioning, not resource duplication. Exceptions require FinOps approval.
**Test:** Check IaC for lifecycle configuration on storage resources.
**Test:** Scan IaC and Helm values for tenant-specific resource naming patterns or parameterized infrastructure per tenant.

View File

@@ -1,36 +1,44 @@
# DevOps Requirements
Phase: deployment
Enforcement: informational (graduating to blocking Q3 2026)
Enforcement: informational
## OPS-001: Health Check Endpoint
## OPS-001: Foxtrot-Compatible Helm Chart
Every deployable service MUST expose a `/health` or `/actuator/health` endpoint that returns 200 when healthy.
Every deployable service MUST include a Helm chart that honors the Foxtrot deployment contract.
**Rule:** New services must include a health check. Existing services adding deployment config must verify health endpoint exists.
**Rule:** The Helm chart must:
- Use the standard Foxtrot base chart as a dependency (or implement its interface)
- Expose `values.yaml` with the required Foxtrot parameters (replicas, resources, env, configMap references)
- Support the standard lifecycle hooks (pre-deploy validation, health check, rollback trigger)
- Not define its own ingress/networking — Foxtrot manages routing
**Test:** Check that service has a health endpoint registered (grep for health route registration).
**Test:** Validate Helm chart structure: check for Foxtrot base chart dependency, required values keys, no ingress resource definitions.
## OPS-002: Structured Logging
## OPS-002: No Infrastructure Provisioning in Service Repos
All log statements MUST use structured logging (JSON format) with at minimum: timestamp, level, service name, correlation ID.
Service repositories MUST NOT provision infrastructure (databases, queues, storage, networking). Infrastructure is managed through the dedicated infrastructure repos.
**Rule:** No `System.out.println`, `console.log` for production logging. Use the logging framework with structured output.
**Rule:** No Terraform, CloudFormation, or Pulumi resource definitions in service repos. Services declare their infrastructure dependencies in a manifest; the platform provisions them.
**Test:** Grep for `System.out.print`, `System.err.print`, raw `console.log` in non-test source files.
**Test:** Scan for `*.tf`, `*.template.yaml` (CFN), `Pulumi.*` files in service repos.
## OPS-003: Resource Limits
## OPS-003: Standard Observability Contract
All Kubernetes deployment manifests MUST specify CPU and memory requests and limits.
Every service MUST expose metrics, health, and readiness endpoints in the standard format.
**Rule:** No unbounded resource consumption. Every container spec must have `resources.requests` and `resources.limits`.
**Rule:**
- `/health` or `/actuator/health` — returns 200 when healthy
- `/ready` or `/actuator/ready` — returns 200 when ready to accept traffic
- Prometheus metrics endpoint at `/metrics` or `/actuator/prometheus`
- Structured JSON logging with correlation ID propagation
**Test:** Parse YAML deployment files, verify `resources` block present with both `requests` and `limits`.
**Test:** Check for health/ready endpoint registration in code. Verify logging config outputs JSON format.
## OPS-004: Rollback Safety
## OPS-004: No Pinned Infrastructure Versions
Database migrations MUST be backward-compatible with the previous service version (N-1 compatibility).
Service Helm charts MUST NOT pin specific infrastructure versions (database versions, queue versions, runtime versions).
**Rule:** No column renames or drops without a multi-step migration. Additive changes only in a single release.
**Rule:** Infrastructure version management is handled by the platform team. Services declare compatibility ranges, not exact versions. No `image: postgres:14.2` in service charts.
**Test:** Scan migration SQL for `DROP COLUMN`, `RENAME COLUMN`, `ALTER TYPE` — flag for manual review.
**Test:** Scan Helm values and templates for hardcoded infrastructure image tags.

View File

@@ -1,54 +1,28 @@
# Security Requirements
Phase: implementation
Enforcement: informational (graduating to blocking Q3 2026)
Enforcement: informational
## SEC-001: Input Validation
## SEC-001: No IAM Resources in Service Repos
All external input (API request bodies, query parameters, headers, file uploads) MUST be validated through a schema validator before processing.
Service repositories MUST NOT contain IAM policies, roles, or identity resources. IAM is centrally managed by the security team through the infrastructure repo.
**Rule:** No raw request body access in business logic. All endpoints must define and validate against a schema (JSON Schema, protobuf, or framework-equivalent).
**Rule:** No Terraform/CloudFormation IAM resource definitions (`aws_iam_role`, `aws_iam_policy`, `google_project_iam_member`, etc.) in service-level repositories. If your service needs a new permission, request it through the IAM change process.
**Test:** Grep for direct `request.body` / `req.body` / `getParameter()` usage outside of controller/validation layer.
**Test:** Scan IaC files for IAM resource type declarations.
```
# Bad
String name = request.getParameter("name");
db.query("SELECT * FROM users WHERE name = '" + name + "'");
## SEC-002: No Embedded Credentials or Auth Bypass
# Good
ValidatedInput input = validator.validate(request, CreateUserSchema.class);
userService.create(input);
```
Services MUST NOT implement their own authentication mechanisms. All auth flows go through the centralized auth service.
## SEC-002: No Raw SQL
**Rule:** No custom JWT validation, no local user tables, no auth middleware that bypasses the platform auth layer. Services consume auth tokens validated by the platform.
All database queries MUST use parameterized queries or an ORM. No string concatenation in SQL statements.
**Test:** Scan for JWT libraries imported outside the auth module, custom `login`/`authenticate` endpoints, local user/password tables in migrations.
**Rule:** Zero tolerance for SQL string concatenation with user-controlled values.
## SEC-003: No Direct External Network Calls Without Proxy
**Test:** Regex scan for SQL keywords adjacent to string concatenation operators (`+`, `concat`, `format`, `f"`, template literals).
Services MUST NOT make direct outbound HTTP calls to external (non-Reltio) endpoints. All external traffic routes through the API gateway/proxy layer.
## SEC-003: Authentication Annotations
**Rule:** Outbound calls to third-party APIs must go through the approved proxy/gateway. No hardcoded external URLs in service code.
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.
**Test:** Scan for HTTP client instantiation with non-internal hostnames.