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
19 changes: 16 additions & 3 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,28 @@
# Default owners for all files
* @kamalsrini

# AI security skills require review from a maintainer with AI security background
# per SECURITY.md Section 4: "Any skill in the ai-security/ directory requires
# review from a maintainer with AI security background"
# Domain skill ownership gates. Replace @kamalsrini or add teams when GitHub
# teams are available; keep these patterns so branch protection can require
# CODEOWNER review by domain.
skills/appsec/ @kamalsrini
skills/cloud/ @kamalsrini
skills/ai-security/ @kamalsrini
skills/compliance/ @kamalsrini
skills/secops/ @kamalsrini

# Adjacent domain surfaces.
skills/devsecops/ @kamalsrini
skills/identity/ @kamalsrini
skills/incident-response/ @kamalsrini
skills/network/ @kamalsrini
skills/vuln-management/ @kamalsrini
roles/ @kamalsrini

# CI/CD workflows require maintainer review (supply chain risk)
.github/workflows/ @kamalsrini

# Index and contributing guidelines require maintainer review
index.yaml @kamalsrini
data/frameworks.yaml @kamalsrini
CONTRIBUTING.md @kamalsrini
SECURITY.md @kamalsrini
17 changes: 17 additions & 0 deletions .github/workflows/validate-codeowners.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Validate CODEOWNERS

on:
pull_request:
push:
branches:
- main

jobs:
validate-codeowners:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Validate required domain review gates
run: ruby scripts/validate_codeowners.rb
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ If your contribution changes CI/CD examples, update
ruby scripts/validate_ci_cd_examples.rb
```

If your contribution changes skill ownership or review routing, update
[.github/CODEOWNERS](.github/CODEOWNERS) and run:

```bash
ruby scripts/validate_codeowners.rb
```

Release artifacts are checksummed by the GitHub release workflow. See
[docs/release-integrity.md](docs/release-integrity.md) before changing release
packaging.
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ ruby scripts/validate_framework_registry.rb
ruby scripts/validate_framework_registry.rb --stale --max-age-days 365
```

Validate required domain CODEOWNERS review gates with:

```bash
ruby scripts/validate_codeowners.rb
```

Release archives include SHA-256 checksums generated by the release workflow.
See [`docs/release-integrity.md`](docs/release-integrity.md) for verification
steps.
Expand Down
46 changes: 46 additions & 0 deletions scripts/validate_codeowners.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

ROOT = File.expand_path("..", __dir__)
CODEOWNERS_PATH = File.join(ROOT, ".github", "CODEOWNERS")
REQUIRED_PATTERNS = {
"AppSec" => "skills/appsec/",
"Cloud" => "skills/cloud/",
"AI Security" => "skills/ai-security/",
"Compliance" => "skills/compliance/",
"SecOps" => "skills/secops/"
}.freeze

def rel(path)
path.delete_prefix("#{ROOT}#{File::SEPARATOR}")
end

errors = []

unless File.file?(CODEOWNERS_PATH)
errors << "#{rel(CODEOWNERS_PATH)}: missing CODEOWNERS file"
else
entries = File.readlines(CODEOWNERS_PATH, chomp: true)
.map(&:strip)
.reject { |line| line.empty? || line.start_with?("#") }

REQUIRED_PATTERNS.each do |domain, pattern|
entry = entries.find { |line| line.split(/\s+/).first == pattern }
if entry.nil?
errors << "#{rel(CODEOWNERS_PATH)}: missing #{domain} owner pattern #{pattern}"
next
end

owners = entry.split(/\s+/)[1..] || []
errors << "#{rel(CODEOWNERS_PATH)}: #{pattern} must list at least one owner" if owners.empty?
end
end

if errors.empty?
puts "OK: CODEOWNERS includes required domain review gates."
else
puts "FAIL: CODEOWNERS validation failed."
errors.each { |error| puts " - #{error}" }
end

exit(errors.empty? ? 0 : 1)
Loading