ma-agents 2.20.3 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/.ma-agents.json +241 -0
- package/.opencode/skills/MANIFEST.yaml +254 -0
- package/.opencode/skills/ai-audit-trail/SKILL.md +23 -0
- package/.opencode/skills/auto-bug-detection/SKILL.md +169 -0
- package/.opencode/skills/cmake-best-practices/SKILL.md +64 -0
- package/.opencode/skills/cmake-best-practices/examples/cmake.md +59 -0
- package/.opencode/skills/code-documentation/SKILL.md +57 -0
- package/.opencode/skills/code-documentation/examples/cpp.md +29 -0
- package/.opencode/skills/code-documentation/examples/csharp.md +28 -0
- package/.opencode/skills/code-documentation/examples/javascript_typescript.md +28 -0
- package/.opencode/skills/code-documentation/examples/python.md +57 -0
- package/.opencode/skills/code-review/SKILL.md +43 -0
- package/.opencode/skills/commit-message/SKILL.md +79 -0
- package/.opencode/skills/cpp-best-practices/SKILL.md +234 -0
- package/.opencode/skills/cpp-best-practices/examples/modern-idioms.md +189 -0
- package/.opencode/skills/cpp-best-practices/examples/naming-and-organization.md +102 -0
- package/.opencode/skills/cpp-concurrency-safety/SKILL.md +60 -0
- package/.opencode/skills/cpp-concurrency-safety/examples/concurrency.md +73 -0
- package/.opencode/skills/cpp-const-correctness/SKILL.md +63 -0
- package/.opencode/skills/cpp-const-correctness/examples/const_correctness.md +54 -0
- package/.opencode/skills/cpp-memory-handling/SKILL.md +42 -0
- package/.opencode/skills/cpp-memory-handling/examples/modern-cpp.md +49 -0
- package/.opencode/skills/cpp-memory-handling/examples/smart-pointers.md +46 -0
- package/.opencode/skills/cpp-modern-composition/SKILL.md +64 -0
- package/.opencode/skills/cpp-modern-composition/examples/composition.md +51 -0
- package/.opencode/skills/cpp-robust-interfaces/SKILL.md +55 -0
- package/.opencode/skills/cpp-robust-interfaces/examples/interfaces.md +56 -0
- package/.opencode/skills/create-hardened-docker-skill/SKILL.md +637 -0
- package/.opencode/skills/create-hardened-docker-skill/scripts/create-all.sh +489 -0
- package/.opencode/skills/csharp-best-practices/SKILL.md +278 -0
- package/.opencode/skills/docker-hardening-verification/SKILL.md +28 -0
- package/.opencode/skills/docker-hardening-verification/scripts/verify-hardening.sh +39 -0
- package/.opencode/skills/docker-image-signing/SKILL.md +28 -0
- package/.opencode/skills/docker-image-signing/scripts/sign-image.sh +33 -0
- package/.opencode/skills/document-revision-history/SKILL.md +104 -0
- package/.opencode/skills/git-workflow-skill/SKILL.md +194 -0
- package/.opencode/skills/git-workflow-skill/hooks/commit-msg +61 -0
- package/.opencode/skills/git-workflow-skill/hooks/pre-commit +38 -0
- package/.opencode/skills/git-workflow-skill/hooks/prepare-commit-msg +56 -0
- package/.opencode/skills/git-workflow-skill/scripts/finish-feature.sh +192 -0
- package/.opencode/skills/git-workflow-skill/scripts/install-hooks.sh +55 -0
- package/.opencode/skills/git-workflow-skill/scripts/start-feature.sh +110 -0
- package/.opencode/skills/git-workflow-skill/scripts/validate-workflow.sh +229 -0
- package/.opencode/skills/js-ts-dependency-mgmt/SKILL.md +49 -0
- package/.opencode/skills/js-ts-dependency-mgmt/examples/dependency_mgmt.md +60 -0
- package/.opencode/skills/js-ts-security-skill/SKILL.md +64 -0
- package/.opencode/skills/js-ts-security-skill/scripts/verify-security.sh +136 -0
- package/.opencode/skills/logging-best-practices/SKILL.md +50 -0
- package/.opencode/skills/logging-best-practices/examples/cpp.md +36 -0
- package/.opencode/skills/logging-best-practices/examples/csharp.md +49 -0
- package/.opencode/skills/logging-best-practices/examples/javascript.md +77 -0
- package/.opencode/skills/logging-best-practices/examples/python.md +57 -0
- package/.opencode/skills/logging-best-practices/references/logging-standards.md +29 -0
- package/.opencode/skills/open-presentation/SKILL.md +35 -0
- package/.opencode/skills/opentelemetry-best-practices/SKILL.md +34 -0
- package/.opencode/skills/opentelemetry-best-practices/examples/go.md +32 -0
- package/.opencode/skills/opentelemetry-best-practices/examples/javascript.md +58 -0
- package/.opencode/skills/opentelemetry-best-practices/examples/python.md +37 -0
- package/.opencode/skills/opentelemetry-best-practices/references/otel-standards.md +37 -0
- package/.opencode/skills/python-best-practices/SKILL.md +385 -0
- package/.opencode/skills/python-dependency-mgmt/SKILL.md +42 -0
- package/.opencode/skills/python-dependency-mgmt/examples/dependency_mgmt.md +67 -0
- package/.opencode/skills/python-security-skill/SKILL.md +56 -0
- package/.opencode/skills/python-security-skill/examples/security.md +56 -0
- package/.opencode/skills/self-signed-cert/SKILL.md +42 -0
- package/.opencode/skills/self-signed-cert/scripts/generate-cert.ps1 +45 -0
- package/.opencode/skills/self-signed-cert/scripts/generate-cert.sh +43 -0
- package/.opencode/skills/skill-creator/SKILL.md +196 -0
- package/.opencode/skills/skill-creator/references/output-patterns.md +82 -0
- package/.opencode/skills/skill-creator/references/workflows.md +28 -0
- package/.opencode/skills/skill-creator/scripts/init_skill.py +208 -0
- package/.opencode/skills/skill-creator/scripts/package_skill.py +99 -0
- package/.opencode/skills/skill-creator/scripts/quick_validate.py +113 -0
- package/.opencode/skills/story-status-lookup/SKILL.md +78 -0
- package/.opencode/skills/test-accompanied-development/SKILL.md +50 -0
- package/.opencode/skills/test-generator/SKILL.md +65 -0
- package/.opencode/skills/vercel-react-best-practices/SKILL.md +109 -0
- package/.opencode/skills/verify-hardened-docker-skill/SKILL.md +442 -0
- package/.opencode/skills/verify-hardened-docker-skill/scripts/verify-docker-hardening.sh +439 -0
- package/AiAudit.md +5 -0
- package/QUICK_START.md +11 -5
- package/README.md +52 -1
- package/bin/cli.js +31 -4
- package/docs/BMAD_AI_Development_Training.pptx +0 -0
- package/docs/technical-notes/context-persistence-research.md +434 -0
- package/docs/technical-notes/enforcement-hooks-research.md +415 -0
- package/lib/agents.js +34 -0
- package/lib/bmad-extension/agents/bmm-architect.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-bmad-master.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-cyber.customize.yaml +30 -0
- package/lib/bmad-extension/agents/bmm-dev.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-devops.customize.yaml +30 -0
- package/lib/bmad-extension/agents/bmm-mil498.customize.yaml +42 -0
- package/lib/bmad-extension/agents/bmm-pm.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-qa.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-sm.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-sre.customize.yaml +30 -0
- package/lib/bmad-extension/agents/bmm-tech-writer.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-ux-designer.customize.yaml +5 -0
- package/lib/bmad-extension/module-help.csv +7 -0
- package/lib/bmad-extension/module.yaml +3 -0
- package/lib/bmad-extension/workflows/add-sprint/workflow.md +112 -0
- package/lib/bmad-extension/workflows/add-to-sprint/workflow.md +206 -0
- package/lib/bmad-extension/workflows/create-bug-story/workflow.md +186 -0
- package/lib/bmad-extension/workflows/modify-sprint/workflow.md +250 -0
- package/lib/bmad-extension/workflows/project-context-expansion/workflow.md +229 -0
- package/lib/bmad-extension/workflows/sprint-status-view/workflow.md +193 -0
- package/lib/bmad.js +168 -36
- package/lib/hooks/claude-code/verify-manifest.js +56 -0
- package/lib/installer.js +282 -1
- package/lib/methodology/BMAD_AI_Development_Training.pptx +0 -0
- package/lib/methodology/version.json +7 -0
- package/lib/skill-authoring.js +732 -0
- package/lib/templates/project-context.template.md +47 -0
- package/opencode.json +8 -0
- package/package.json +2 -2
- package/skills/auto-bug-detection/SKILL.md +165 -0
- package/skills/auto-bug-detection/skill.json +8 -0
- package/skills/code-review/SKILL.md +40 -0
- package/skills/cpp-best-practices/SKILL.md +230 -0
- package/skills/cpp-best-practices/examples/modern-idioms.md +189 -0
- package/skills/cpp-best-practices/examples/naming-and-organization.md +102 -0
- package/skills/cpp-best-practices/skill.json +25 -0
- package/skills/csharp-best-practices/SKILL.md +274 -0
- package/skills/csharp-best-practices/skill.json +23 -0
- package/skills/git-workflow-skill/skill.json +1 -1
- package/skills/open-presentation/SKILL.md +31 -0
- package/skills/open-presentation/skill.json +11 -0
- package/skills/python-best-practices/SKILL.md +381 -0
- package/skills/python-best-practices/skill.json +26 -0
- package/skills/story-status-lookup/SKILL.md +74 -0
- package/skills/story-status-lookup/skill.json +8 -0
- package/test/agent-injection-strategy.test.js +13 -7
- package/test/bmad-extension.test.js +237 -0
- package/test/bmad-output-policy.test.js +119 -0
- package/test/build-bmad-args.test.js +361 -0
- package/test/create-agent.test.js +232 -0
- package/test/enforcement-hooks.test.js +324 -0
- package/test/generate-project-context.test.js +337 -0
- package/test/integration-verification.test.js +402 -0
- package/test/opencode-agent.test.js +150 -0
- package/test/opencode-json-error.test.js +260 -0
- package/test/opencode-json-injection.test.js +256 -0
- package/test/opencode-json-merge.test.js +299 -0
- package/test/skill-authoring.test.js +272 -0
- package/test/skill-customize-agent.test.js +253 -0
- package/test/skill-mandatory.test.js +235 -0
- package/test/skill-validation.test.js +378 -0
- package/test/yes-flag.test.js +1 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# verify-docker-hardening.sh
|
|
4
|
+
# Comprehensive Docker security verification script
|
|
5
|
+
# Checks Dockerfile, docker-compose.yml, and running containers
|
|
6
|
+
# against CIS, OWASP, and NIST standards
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
# Colors for output
|
|
12
|
+
RED='\033[0;31m'
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
BLUE='\033[0;34m'
|
|
16
|
+
NC='\033[0m' # No Color
|
|
17
|
+
|
|
18
|
+
# Configuration
|
|
19
|
+
IMAGE_NAME="${1:-contacts-app}"
|
|
20
|
+
CONTAINER_NAME="${2:-contacts-app}"
|
|
21
|
+
EXIT_CODE=0
|
|
22
|
+
|
|
23
|
+
# Counters
|
|
24
|
+
TOTAL_CHECKS=0
|
|
25
|
+
PASSED_CHECKS=0
|
|
26
|
+
FAILED_CHECKS=0
|
|
27
|
+
WARNING_CHECKS=0
|
|
28
|
+
|
|
29
|
+
# Helper functions
|
|
30
|
+
print_header() {
|
|
31
|
+
echo ""
|
|
32
|
+
echo -e "${BLUE}========================================${NC}"
|
|
33
|
+
echo -e "${BLUE}$1${NC}"
|
|
34
|
+
echo -e "${BLUE}========================================${NC}"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
print_check() {
|
|
38
|
+
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
|
|
39
|
+
echo -ne " [$TOTAL_CHECKS] $1... "
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pass() {
|
|
43
|
+
PASSED_CHECKS=$((PASSED_CHECKS + 1))
|
|
44
|
+
echo -e "${GREEN}✅ PASS${NC}"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fail() {
|
|
48
|
+
FAILED_CHECKS=$((FAILED_CHECKS + 1))
|
|
49
|
+
EXIT_CODE=2
|
|
50
|
+
echo -e "${RED}❌ FAIL${NC}"
|
|
51
|
+
[ -n "$1" ] && echo -e " ${RED}→ $1${NC}"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
warn() {
|
|
55
|
+
WARNING_CHECKS=$((WARNING_CHECKS + 1))
|
|
56
|
+
echo -e "${YELLOW}⚠️ WARN${NC}"
|
|
57
|
+
[ -n "$1" ] && echo -e " ${YELLOW}→ $1${NC}"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
critical() {
|
|
61
|
+
FAILED_CHECKS=$((FAILED_CHECKS + 1))
|
|
62
|
+
EXIT_CODE=1
|
|
63
|
+
echo -e "${RED}🚨 CRITICAL${NC}"
|
|
64
|
+
[ -n "$1" ] && echo -e " ${RED}→ $1${NC}"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Start verification
|
|
68
|
+
echo -e "${BLUE}🔍 Docker Security Verification${NC}"
|
|
69
|
+
echo -e "${BLUE}================================${NC}"
|
|
70
|
+
echo "Image: $IMAGE_NAME"
|
|
71
|
+
echo "Container: $CONTAINER_NAME"
|
|
72
|
+
|
|
73
|
+
# ============================================================================
|
|
74
|
+
# 1. Dockerfile Verification
|
|
75
|
+
# ============================================================================
|
|
76
|
+
print_header "1. Dockerfile Security"
|
|
77
|
+
|
|
78
|
+
if [ ! -f "Dockerfile" ]; then
|
|
79
|
+
print_check "Dockerfile exists"
|
|
80
|
+
critical "Dockerfile not found in current directory"
|
|
81
|
+
EXIT_CODE=5
|
|
82
|
+
exit $EXIT_CODE
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Check for specific version tags
|
|
86
|
+
print_check "Specific version tags (no :latest)"
|
|
87
|
+
if grep -qE "^FROM.*:(latest|alpine)$" Dockerfile; then
|
|
88
|
+
fail "Using :latest or unversioned :alpine tag"
|
|
89
|
+
else
|
|
90
|
+
pass
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Check for non-root user
|
|
94
|
+
print_check "Non-root user configured"
|
|
95
|
+
if grep -qE "^USER (root|[0-9]+)" Dockerfile; then
|
|
96
|
+
if grep -qE "^USER root" Dockerfile; then
|
|
97
|
+
fail "Running as root user"
|
|
98
|
+
else
|
|
99
|
+
pass
|
|
100
|
+
fi
|
|
101
|
+
elif grep -qE "^USER [a-zA-Z]" Dockerfile; then
|
|
102
|
+
pass
|
|
103
|
+
else
|
|
104
|
+
fail "No USER directive found"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Check for HEALTHCHECK
|
|
108
|
+
print_check "HEALTHCHECK instruction"
|
|
109
|
+
if grep -q "^HEALTHCHECK" Dockerfile; then
|
|
110
|
+
pass
|
|
111
|
+
else
|
|
112
|
+
warn "Missing HEALTHCHECK instruction"
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Check for hardcoded secrets
|
|
116
|
+
print_check "No hardcoded secrets in ENV/ARG"
|
|
117
|
+
if grep -iE "^(ENV|ARG).*(SECRET|PASSWORD|KEY|TOKEN|CLIENT_ID)=" Dockerfile | grep -v "REACT_APP_API_BASE_URL" > /dev/null; then
|
|
118
|
+
fail "Potential hardcoded secrets found"
|
|
119
|
+
else
|
|
120
|
+
pass
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Check for multi-stage build
|
|
124
|
+
print_check "Multi-stage build pattern"
|
|
125
|
+
if [ $(grep -c "^FROM" Dockerfile) -ge 2 ]; then
|
|
126
|
+
pass
|
|
127
|
+
else
|
|
128
|
+
warn "Not using multi-stage build"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Check for Alpine base images
|
|
132
|
+
print_check "Minimal Alpine base images"
|
|
133
|
+
if grep -qE "^FROM.*alpine" Dockerfile; then
|
|
134
|
+
pass
|
|
135
|
+
else
|
|
136
|
+
warn "Not using Alpine base images"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# ============================================================================
|
|
140
|
+
# 2. docker-compose.yml Verification
|
|
141
|
+
# ============================================================================
|
|
142
|
+
print_header "2. docker-compose.yml Security"
|
|
143
|
+
|
|
144
|
+
if [ ! -f "docker-compose.yml" ]; then
|
|
145
|
+
print_check "docker-compose.yml exists"
|
|
146
|
+
warn "docker-compose.yml not found (optional)"
|
|
147
|
+
else
|
|
148
|
+
# Check for read-only filesystem
|
|
149
|
+
print_check "Read-only root filesystem"
|
|
150
|
+
if grep -q "read_only: true" docker-compose.yml; then
|
|
151
|
+
pass
|
|
152
|
+
else
|
|
153
|
+
fail "Missing read_only: true"
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Check for no-new-privileges
|
|
157
|
+
print_check "No new privileges"
|
|
158
|
+
if grep -q "no-new-privileges:true" docker-compose.yml; then
|
|
159
|
+
pass
|
|
160
|
+
else
|
|
161
|
+
fail "Missing security_opt: no-new-privileges:true"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Check for capability dropping
|
|
165
|
+
print_check "Capabilities dropped"
|
|
166
|
+
if grep -q "cap_drop:" docker-compose.yml; then
|
|
167
|
+
pass
|
|
168
|
+
else
|
|
169
|
+
fail "Missing cap_drop configuration"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# Check for tmpfs mounts
|
|
173
|
+
print_check "Tmpfs mounts for writable dirs"
|
|
174
|
+
if grep -q "tmpfs:" docker-compose.yml; then
|
|
175
|
+
pass
|
|
176
|
+
else
|
|
177
|
+
fail "Missing tmpfs mounts"
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Check for resource limits
|
|
181
|
+
print_check "Memory limits configured"
|
|
182
|
+
if grep -q "memory:" docker-compose.yml; then
|
|
183
|
+
pass
|
|
184
|
+
else
|
|
185
|
+
warn "Missing memory limits"
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
print_check "CPU limits configured"
|
|
189
|
+
if grep -q "cpus:" docker-compose.yml; then
|
|
190
|
+
pass
|
|
191
|
+
else
|
|
192
|
+
warn "Missing CPU limits"
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# Check for health check
|
|
196
|
+
print_check "Healthcheck configured"
|
|
197
|
+
if grep -q "healthcheck:" docker-compose.yml; then
|
|
198
|
+
pass
|
|
199
|
+
else
|
|
200
|
+
warn "Missing healthcheck configuration"
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# Check for privileged mode
|
|
204
|
+
print_check "Not running in privileged mode"
|
|
205
|
+
if grep -q "privileged: true" docker-compose.yml; then
|
|
206
|
+
critical "Running in privileged mode"
|
|
207
|
+
else
|
|
208
|
+
pass
|
|
209
|
+
fi
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
# ============================================================================
|
|
213
|
+
# 3. .dockerignore Verification
|
|
214
|
+
# ============================================================================
|
|
215
|
+
print_header "3. .dockerignore Configuration"
|
|
216
|
+
|
|
217
|
+
if [ ! -f ".dockerignore" ]; then
|
|
218
|
+
print_check ".dockerignore exists"
|
|
219
|
+
warn ".dockerignore not found"
|
|
220
|
+
else
|
|
221
|
+
print_check ".env excluded from Docker context"
|
|
222
|
+
if grep -qE "^\.env$" .dockerignore; then
|
|
223
|
+
pass
|
|
224
|
+
else
|
|
225
|
+
critical ".env not in .dockerignore - secret leakage risk!"
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
print_check "node_modules excluded"
|
|
229
|
+
if grep -q "node_modules" .dockerignore; then
|
|
230
|
+
pass
|
|
231
|
+
else
|
|
232
|
+
warn "node_modules not excluded"
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
print_check ".git excluded"
|
|
236
|
+
if grep -qE "^\.git" .dockerignore; then
|
|
237
|
+
pass
|
|
238
|
+
else
|
|
239
|
+
warn ".git not excluded"
|
|
240
|
+
fi
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
# ============================================================================
|
|
244
|
+
# 4. Image Security Scanning
|
|
245
|
+
# ============================================================================
|
|
246
|
+
print_header "4. Image Vulnerability Scanning"
|
|
247
|
+
|
|
248
|
+
# Check if image exists
|
|
249
|
+
if ! docker images --format "{{.Repository}}" | grep -q "^${IMAGE_NAME}$"; then
|
|
250
|
+
print_check "Docker image exists"
|
|
251
|
+
warn "Image '$IMAGE_NAME' not found locally. Skipping image scans."
|
|
252
|
+
echo " Run 'docker build -t $IMAGE_NAME .' to build the image."
|
|
253
|
+
else
|
|
254
|
+
# Check if trivy is installed
|
|
255
|
+
if ! command -v trivy &> /dev/null; then
|
|
256
|
+
print_check "Trivy scanner installed"
|
|
257
|
+
warn "Trivy not installed. Skipping vulnerability scans."
|
|
258
|
+
echo " Install: brew install aquasecurity/trivy/trivy (macOS)"
|
|
259
|
+
echo " : apt-get install trivy (Linux)"
|
|
260
|
+
else
|
|
261
|
+
# Scan for CRITICAL vulnerabilities
|
|
262
|
+
print_check "No CRITICAL vulnerabilities"
|
|
263
|
+
if trivy image --quiet --severity CRITICAL --exit-code 1 "$IMAGE_NAME" > /dev/null 2>&1; then
|
|
264
|
+
pass
|
|
265
|
+
else
|
|
266
|
+
critical "CRITICAL vulnerabilities found. Run: trivy image --severity CRITICAL $IMAGE_NAME"
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
# Scan for HIGH vulnerabilities
|
|
270
|
+
print_check "No HIGH vulnerabilities"
|
|
271
|
+
if trivy image --quiet --severity HIGH --exit-code 1 "$IMAGE_NAME" > /dev/null 2>&1; then
|
|
272
|
+
pass
|
|
273
|
+
else
|
|
274
|
+
fail "HIGH vulnerabilities found. Run: trivy image --severity HIGH $IMAGE_NAME"
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
# Scan for leaked secrets
|
|
278
|
+
print_check "No leaked secrets in image"
|
|
279
|
+
if trivy image --quiet --scanners secret --exit-code 1 "$IMAGE_NAME" > /dev/null 2>&1; then
|
|
280
|
+
pass
|
|
281
|
+
else
|
|
282
|
+
critical "Secrets detected in image! Run: trivy image --scanners secret $IMAGE_NAME"
|
|
283
|
+
fi
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
# Check image size
|
|
287
|
+
print_check "Optimized image size (< 100MB)"
|
|
288
|
+
IMAGE_SIZE=$(docker images --format "{{.Size}}" "$IMAGE_NAME" | head -1 | sed 's/MB//' | sed 's/GB/*1024/' | bc 2>/dev/null || echo "0")
|
|
289
|
+
if [ -n "$IMAGE_SIZE" ] && [ "$IMAGE_SIZE" != "0" ]; then
|
|
290
|
+
if (( $(echo "$IMAGE_SIZE < 100" | bc -l) )); then
|
|
291
|
+
pass
|
|
292
|
+
else
|
|
293
|
+
warn "Image size is ${IMAGE_SIZE}MB (recommended < 100MB)"
|
|
294
|
+
fi
|
|
295
|
+
else
|
|
296
|
+
warn "Could not determine image size"
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
# Check for .env in image
|
|
300
|
+
print_check ".env file not baked into image"
|
|
301
|
+
if docker run --rm "$IMAGE_NAME" sh -c "ls -la / 2>/dev/null | grep -q .env"; then
|
|
302
|
+
critical ".env file found in image! Secrets leaked!"
|
|
303
|
+
else
|
|
304
|
+
pass
|
|
305
|
+
fi
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
# ============================================================================
|
|
309
|
+
# 5. Runtime Security (if container is running)
|
|
310
|
+
# ============================================================================
|
|
311
|
+
print_header "5. Runtime Security Verification"
|
|
312
|
+
|
|
313
|
+
if ! docker ps --filter "name=$CONTAINER_NAME" --format "{{.Names}}" | grep -q "^$CONTAINER_NAME$"; then
|
|
314
|
+
echo -e "${YELLOW} Container '$CONTAINER_NAME' is not running.${NC}"
|
|
315
|
+
echo -e "${YELLOW} Run 'docker-compose up -d' to enable runtime checks.${NC}"
|
|
316
|
+
else
|
|
317
|
+
# Check container runs as non-root
|
|
318
|
+
print_check "Container runs as non-root"
|
|
319
|
+
CONTAINER_USER=$(docker exec "$CONTAINER_NAME" whoami 2>/dev/null || echo "root")
|
|
320
|
+
if [ "$CONTAINER_USER" != "root" ]; then
|
|
321
|
+
pass
|
|
322
|
+
else
|
|
323
|
+
critical "Container running as root user!"
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
# Check user ID is not 0
|
|
327
|
+
print_check "User ID is not 0 (root)"
|
|
328
|
+
USER_ID=$(docker exec "$CONTAINER_NAME" id -u 2>/dev/null || echo "0")
|
|
329
|
+
if [ "$USER_ID" != "0" ]; then
|
|
330
|
+
pass
|
|
331
|
+
else
|
|
332
|
+
critical "Container running with UID 0 (root)!"
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
# Check read-only filesystem
|
|
336
|
+
print_check "Root filesystem is read-only"
|
|
337
|
+
if docker exec "$CONTAINER_NAME" sh -c "touch /test 2>/dev/null"; then
|
|
338
|
+
fail "Root filesystem is writable"
|
|
339
|
+
docker exec "$CONTAINER_NAME" sh -c "rm /test 2>/dev/null" || true
|
|
340
|
+
else
|
|
341
|
+
pass
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# Check tmpfs is writable
|
|
345
|
+
print_check "Tmpfs mount is writable"
|
|
346
|
+
if docker exec "$CONTAINER_NAME" sh -c "touch /tmp/test 2>/dev/null && rm /tmp/test 2>/dev/null"; then
|
|
347
|
+
pass
|
|
348
|
+
else
|
|
349
|
+
warn "Tmpfs mount /tmp is not writable"
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
# Check health status
|
|
353
|
+
print_check "Container is healthy"
|
|
354
|
+
HEALTH_STATUS=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || echo "none")
|
|
355
|
+
if [ "$HEALTH_STATUS" = "healthy" ]; then
|
|
356
|
+
pass
|
|
357
|
+
elif [ "$HEALTH_STATUS" = "none" ]; then
|
|
358
|
+
warn "No health check configured"
|
|
359
|
+
else
|
|
360
|
+
fail "Container health status: $HEALTH_STATUS"
|
|
361
|
+
fi
|
|
362
|
+
|
|
363
|
+
# Check capabilities
|
|
364
|
+
print_check "Capabilities dropped"
|
|
365
|
+
CAPS_DROPPED=$(docker inspect --format='{{.HostConfig.CapDrop}}' "$CONTAINER_NAME" 2>/dev/null || echo "[]")
|
|
366
|
+
if echo "$CAPS_DROPPED" | grep -q "ALL"; then
|
|
367
|
+
pass
|
|
368
|
+
else
|
|
369
|
+
warn "Not all capabilities dropped"
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
# Check memory limit
|
|
373
|
+
print_check "Memory limit enforced"
|
|
374
|
+
MEMORY_LIMIT=$(docker inspect --format='{{.HostConfig.Memory}}' "$CONTAINER_NAME" 2>/dev/null || echo "0")
|
|
375
|
+
if [ "$MEMORY_LIMIT" != "0" ]; then
|
|
376
|
+
pass
|
|
377
|
+
else
|
|
378
|
+
warn "No memory limit set"
|
|
379
|
+
fi
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
# ============================================================================
|
|
383
|
+
# 6. Git Secret Protection
|
|
384
|
+
# ============================================================================
|
|
385
|
+
print_header "6. Git Secret Protection"
|
|
386
|
+
|
|
387
|
+
if [ -d ".git" ]; then
|
|
388
|
+
# Check .env in .gitignore
|
|
389
|
+
print_check ".env in .gitignore"
|
|
390
|
+
if [ -f ".gitignore" ] && grep -qE "^\.env$" .gitignore; then
|
|
391
|
+
pass
|
|
392
|
+
else
|
|
393
|
+
critical ".env not in .gitignore! Secrets may be committed!"
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
# Check .env.example exists
|
|
397
|
+
print_check ".env.example exists (template)"
|
|
398
|
+
if [ -f ".env.example" ]; then
|
|
399
|
+
pass
|
|
400
|
+
else
|
|
401
|
+
warn ".env.example not found"
|
|
402
|
+
fi
|
|
403
|
+
|
|
404
|
+
# Check if .env is committed
|
|
405
|
+
print_check ".env not committed to git"
|
|
406
|
+
if git ls-files --error-unmatch .env > /dev/null 2>&1; then
|
|
407
|
+
critical ".env is tracked by git! Remove immediately!"
|
|
408
|
+
else
|
|
409
|
+
pass
|
|
410
|
+
fi
|
|
411
|
+
else
|
|
412
|
+
echo -e "${YELLOW} Not a git repository. Skipping git checks.${NC}"
|
|
413
|
+
fi
|
|
414
|
+
|
|
415
|
+
# ============================================================================
|
|
416
|
+
# Summary
|
|
417
|
+
# ============================================================================
|
|
418
|
+
print_header "Verification Summary"
|
|
419
|
+
|
|
420
|
+
echo ""
|
|
421
|
+
echo " Total checks: $TOTAL_CHECKS"
|
|
422
|
+
echo -e " ${GREEN}Passed: $PASSED_CHECKS${NC}"
|
|
423
|
+
echo -e " ${YELLOW}Warnings: $WARNING_CHECKS${NC}"
|
|
424
|
+
echo -e " ${RED}Failed: $FAILED_CHECKS${NC}"
|
|
425
|
+
echo ""
|
|
426
|
+
|
|
427
|
+
if [ $FAILED_CHECKS -eq 0 ]; then
|
|
428
|
+
echo -e "${GREEN}✅ All critical security checks passed!${NC}"
|
|
429
|
+
if [ $WARNING_CHECKS -gt 0 ]; then
|
|
430
|
+
echo -e "${YELLOW}⚠️ $WARNING_CHECKS warning(s) - consider addressing these.${NC}"
|
|
431
|
+
fi
|
|
432
|
+
else
|
|
433
|
+
echo -e "${RED}❌ $FAILED_CHECKS security check(s) failed!${NC}"
|
|
434
|
+
echo -e "${RED} Please fix the issues above before deploying.${NC}"
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
echo ""
|
|
438
|
+
|
|
439
|
+
exit $EXIT_CODE
|
package/AiAudit.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# AI Audit Trail
|
|
2
|
+
|
|
3
|
+
| Date | Task | Tokens (est.) |
|
|
4
|
+
|------|------|---------------|
|
|
5
|
+
| 2026-03-18 | Story 9.2: JSON Injection — Create opencode.json When Absent. Added MA_AGENTS_SOURCE constant, json-merge branch in updateAgentInstructions(), and 12-test fixture suite. | In: ~18K / Out: ~6K |
|
package/QUICK_START.md
CHANGED
|
@@ -7,14 +7,14 @@ Get started with AI Agent Skills in 2 minutes!
|
|
|
7
7
|
No installation needed! Just run:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx
|
|
10
|
+
npx ma-agents
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Using the CLI
|
|
14
14
|
|
|
15
15
|
1. **Run the tool:**
|
|
16
16
|
```bash
|
|
17
|
-
npx
|
|
17
|
+
npx ma-agents
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
2. **Select "Install a skill"**
|
|
@@ -38,23 +38,29 @@ npx ai-agent-skills
|
|
|
38
38
|
|
|
39
39
|
6. **Done!** The skill is now available in your selected agents.
|
|
40
40
|
|
|
41
|
+
> **Project-level install also generates `_bmad-output/project-context.md`** — a constitutional
|
|
42
|
+
> document that tells AI agents which skills to load and how to run missions in this project.
|
|
43
|
+
> It is never overwritten on reinstall, and its MANIFEST paths are kept up to date on every
|
|
44
|
+
> subsequent install. See [Project Context](README.md#project-context) in the README for details
|
|
45
|
+
> on what it contains and how to expand it over time.
|
|
46
|
+
|
|
41
47
|
## Quick Examples
|
|
42
48
|
|
|
43
49
|
### List all available skills
|
|
44
50
|
```bash
|
|
45
|
-
npx
|
|
51
|
+
npx ma-agents
|
|
46
52
|
# Select: List available skills
|
|
47
53
|
```
|
|
48
54
|
|
|
49
55
|
### List supported agents
|
|
50
56
|
```bash
|
|
51
|
-
npx
|
|
57
|
+
npx ma-agents
|
|
52
58
|
# Select: List supported agents
|
|
53
59
|
```
|
|
54
60
|
|
|
55
61
|
### Install a skill
|
|
56
62
|
```bash
|
|
57
|
-
npx
|
|
63
|
+
npx ma-agents
|
|
58
64
|
# Select: Install a skill
|
|
59
65
|
# Choose: code-review
|
|
60
66
|
# Select agents: claude-code, cline
|
package/README.md
CHANGED
|
@@ -51,6 +51,56 @@ skills/code-review/
|
|
|
51
51
|
+ Updated Agent Instructions (CLAUDE.md, etc.)
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
## Project Knowledge: _bmad-output/
|
|
55
|
+
|
|
56
|
+
The `_bmad-output/` folder is **intentionally tracked in version control** as project knowledge. Do not add `_bmad-output/` to your `.gitignore`.
|
|
57
|
+
|
|
58
|
+
### What it contains
|
|
59
|
+
|
|
60
|
+
`_bmad-output/` holds all AI-generated planning artifacts produced by BMAD agents:
|
|
61
|
+
|
|
62
|
+
- **`planning-artifacts/`** — PRDs, architecture documents, UX designs
|
|
63
|
+
- **`implementation-artifacts/`** — Epics, user stories, sprint status
|
|
64
|
+
- **`methodology/`** — BMAD-METHOD training materials and onboarding presentation
|
|
65
|
+
|
|
66
|
+
### Why it is version-controlled
|
|
67
|
+
|
|
68
|
+
1. **Team alignment** — all team members see the same AI-generated plans and decisions
|
|
69
|
+
2. **AI context continuity** — AI agents need prior context across sessions to maintain consistency
|
|
70
|
+
3. **Decision history** — preserves the rationale and evolution of architectural and product decisions
|
|
71
|
+
|
|
72
|
+
### Installer behaviour
|
|
73
|
+
|
|
74
|
+
The `ma-agents` installer automatically removes `_bmad-output/` from `.gitignore` if it finds it there. This is intentional — if your IDE or tooling suggests adding `_bmad-output/` to `.gitignore`, you should decline. The folder is project knowledge, not build artefacts.
|
|
75
|
+
|
|
76
|
+
> **Do not add `_bmad-output/` to your `.gitignore`** — the installer will remove it automatically if it appears there.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Project Context
|
|
81
|
+
|
|
82
|
+
When you run `npx ma-agents install` at the project level, ma-agents automatically generates
|
|
83
|
+
`_bmad-output/project-context.md` — a constitutional document that governs how AI agents
|
|
84
|
+
behave in this project.
|
|
85
|
+
|
|
86
|
+
**What it contains:**
|
|
87
|
+
- Mandatory pre-task rules: which MANIFEST.yaml files to read, which skills to load
|
|
88
|
+
- Git workflow mandate: fresh worktrees per mission (not just branches)
|
|
89
|
+
- Agent mission lifecycle: 8-step sequence from pre-flight to post-mission cleanup
|
|
90
|
+
|
|
91
|
+
**It is safe to edit.** The installer never regenerates the file from scratch once it exists.
|
|
92
|
+
On reinstall, only the `## AI Agent Mandatory Rules` MANIFEST paths section is updated
|
|
93
|
+
if new agents have been added — all your custom edits are preserved. Edit it freely — it belongs to your project.
|
|
94
|
+
|
|
95
|
+
**Expand it over time:**
|
|
96
|
+
- After architecture phase: run `/bmad-generate-project-context` to auto-detect your stack
|
|
97
|
+
- After each sprint: run `/bmad-retrospective`, then `/project-context-expansion`
|
|
98
|
+
- Manually: add conventions you notice agents getting wrong
|
|
99
|
+
|
|
100
|
+
The file is version-controlled as part of `_bmad-output/` project knowledge. Commit it.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
54
104
|
## Supported Agents
|
|
55
105
|
|
|
56
106
|
| Agent | Project Path | Template | Instruction File |
|
|
@@ -67,7 +117,7 @@ skills/code-review/
|
|
|
67
117
|
| Joseph (MIL-STD-498) | `_bmad/skills/` | `generic` | `_bmad/bmm/agents/mil498.md` |
|
|
68
118
|
| Antigravity | `_bmad/skills/` | `generic` | `_bmad/bmm/agents/antigravity.md` |
|
|
69
119
|
|
|
70
|
-
## Available Skills (
|
|
120
|
+
## Available Skills (28)
|
|
71
121
|
|
|
72
122
|
| Skill ID | Domain | Description |
|
|
73
123
|
| :--- | :--- | :--- |
|
|
@@ -80,6 +130,7 @@ skills/code-review/
|
|
|
80
130
|
| `skill-creator` | Meta | Guide for creating new skills to extend agent capabilities |
|
|
81
131
|
| `document-revision-history` | Documentation | Manages revision history tables at the beginning of generated documents |
|
|
82
132
|
| `ai-audit-trail` | Audit | Tracks AI agent session activity, time spent, and token usage |
|
|
133
|
+
| `open-presentation` | Methodology | Opens the BMAD-METHOD AI Development Training presentation (`/open-presentation`) |
|
|
83
134
|
| **Security** | | |
|
|
84
135
|
| `python-security-skill` | Python | OWASP Top 10 2025 aligned guardrails for Python |
|
|
85
136
|
| `js-ts-security-skill` | JS/TS | Security verification for JS/TS codebases (OWASP aligned) |
|
package/bin/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ const chalk = require('chalk');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { installSkill, uninstallSkill, getStatus, listSkills, listAgents } = require('../lib/installer');
|
|
7
7
|
const bmad = require('../lib/bmad');
|
|
8
|
+
const { handleCreateSkill, handleValidateSkill, handleSetMandatory, handleCustomizeAgent, handleCreateAgent } = require('../lib/skill-authoring');
|
|
8
9
|
|
|
9
10
|
const PKG = require('../package.json');
|
|
10
11
|
const NAME = PKG.name;
|
|
@@ -22,6 +23,11 @@ ${chalk.bold('Usage:')}
|
|
|
22
23
|
${chalk.cyan(`npx ${NAME} status`)} Show installed skills
|
|
23
24
|
${chalk.cyan(`npx ${NAME} list`)} List available skills
|
|
24
25
|
${chalk.cyan(`npx ${NAME} agents`)} List supported agents
|
|
26
|
+
${chalk.cyan(`npx ${NAME} create-skill`)} <name> Scaffold a new skill directory
|
|
27
|
+
${chalk.cyan(`npx ${NAME} validate-skill`)} <name> Validate a skill against the schema
|
|
28
|
+
${chalk.cyan(`npx ${NAME} set-mandatory`)} <name> [--off] Mark a skill as always-load (or remove)
|
|
29
|
+
${chalk.cyan(`npx ${NAME} customize-agent`)} <agent> Customize a BMAD agent persona and actions
|
|
30
|
+
${chalk.cyan(`npx ${NAME} create-agent`)} <name> Create a new specialized BMAD agent
|
|
25
31
|
${chalk.cyan(`npx ${NAME} help`)} Show this help
|
|
26
32
|
|
|
27
33
|
${chalk.bold('Install options:')}
|
|
@@ -44,9 +50,15 @@ ${chalk.bold('Examples:')}
|
|
|
44
50
|
`);
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
function showSkills() {
|
|
48
|
-
const
|
|
49
|
-
|
|
53
|
+
function showSkills(args) {
|
|
54
|
+
const mandatoryOnly = args && args.includes('--mandatory');
|
|
55
|
+
let skills = listSkills();
|
|
56
|
+
if (mandatoryOnly) {
|
|
57
|
+
skills = skills.filter(s => s.always_load === true);
|
|
58
|
+
console.log(chalk.bold('\n Mandatory Skills (always_load: true):\n'));
|
|
59
|
+
} else {
|
|
60
|
+
console.log(chalk.bold('\n Available Skills:\n'));
|
|
61
|
+
}
|
|
50
62
|
skills.forEach(skill => {
|
|
51
63
|
console.log(chalk.cyan(` ${skill.id.padEnd(35)}`) + chalk.white(`${skill.name}`) + chalk.gray(` v${skill.version}`));
|
|
52
64
|
console.log(chalk.gray(` ${''.padEnd(35)}${skill.description}\n`));
|
|
@@ -632,12 +644,27 @@ async function main() {
|
|
|
632
644
|
break;
|
|
633
645
|
case 'list':
|
|
634
646
|
case 'list-skills':
|
|
635
|
-
showSkills();
|
|
647
|
+
showSkills(args.slice(1));
|
|
636
648
|
break;
|
|
637
649
|
case 'agents':
|
|
638
650
|
case 'list-agents':
|
|
639
651
|
showAgents();
|
|
640
652
|
break;
|
|
653
|
+
case 'create-skill':
|
|
654
|
+
await handleCreateSkill(args.slice(1));
|
|
655
|
+
break;
|
|
656
|
+
case 'validate-skill':
|
|
657
|
+
await handleValidateSkill(args.slice(1));
|
|
658
|
+
break;
|
|
659
|
+
case 'set-mandatory':
|
|
660
|
+
await handleSetMandatory(args.slice(1));
|
|
661
|
+
break;
|
|
662
|
+
case 'customize-agent':
|
|
663
|
+
await handleCustomizeAgent(args.slice(1));
|
|
664
|
+
break;
|
|
665
|
+
case 'create-agent':
|
|
666
|
+
await handleCreateAgent(args.slice(1));
|
|
667
|
+
break;
|
|
641
668
|
case 'help':
|
|
642
669
|
case '--help':
|
|
643
670
|
case '-h':
|
|
Binary file
|