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:
6
.demo/dependencies.txt
Normal file
6
.demo/dependencies.txt
Normal 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
28
.demo/infra/main.tf
Normal 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"
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
168
.tests/check.sh
168
.tests/check.sh
@@ -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
|
||||
# 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: 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 SQL string concatenation found"
|
||||
pass "No direct external network calls"
|
||||
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
|
||||
echo ""
|
||||
|
||||
# ── 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
|
||||
# ── 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 hardcoded secrets detected"
|
||||
pass "No direct cloud SDK imports"
|
||||
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
|
||||
# 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 raw stdout/stderr logging"
|
||||
pass "No cross-service database access detected"
|
||||
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"
|
||||
# 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 "Resource limits present: $f"
|
||||
pass "No hardcoded tenant/environment configuration"
|
||||
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)"
|
||||
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
|
||||
done
|
||||
if [[ -z "$MIGRATIONS" ]]; then pass "No migration files in scope"; fi
|
||||
|
||||
# ── COST-001: Resource Tagging ──
|
||||
# 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 "═══════════════════════════════════════"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user