ginskill-init 1.0.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/README.md +77 -0
- package/agents/developer.md +56 -0
- package/agents/frontend-design.md +69 -0
- package/agents/mobile-reviewer.md +36 -0
- package/agents/review-code.md +49 -0
- package/agents/security-scanner.md +50 -0
- package/agents/tester.md +72 -0
- package/bin/cli.js +226 -0
- package/package.json +20 -0
- package/skills/ai-asset-generator/SKILL.md +255 -0
- package/skills/ai-asset-generator/docs/gen-image.md +274 -0
- package/skills/ai-asset-generator/docs/genvideo.md +341 -0
- package/skills/ai-asset-generator/docs/remove-background.md +19 -0
- package/skills/ai-asset-generator/generate-credit-assets.mjs +180 -0
- package/skills/ai-asset-generator/generate-ginbrowser-assets.mjs +242 -0
- package/skills/ai-asset-generator/generate-sty-icon.mjs +149 -0
- package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
- package/skills/ai-asset-generator/lib/env.mjs +38 -0
- package/skills/ai-asset-generator/lib/kie-client.mjs +88 -0
- package/skills/ai-asset-generator/scripts/scaffold-generator.mjs +203 -0
- package/skills/ai-build-ai/SKILL.md +124 -0
- package/skills/ai-build-ai/docs/agent-teams.md +293 -0
- package/skills/ai-build-ai/docs/checkpointing.md +161 -0
- package/skills/ai-build-ai/docs/create-agent.md +399 -0
- package/skills/ai-build-ai/docs/create-mcp.md +395 -0
- package/skills/ai-build-ai/docs/create-skill.md +299 -0
- package/skills/ai-build-ai/docs/headless-mode.md +614 -0
- package/skills/ai-build-ai/docs/hooks.md +578 -0
- package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
- package/skills/ai-build-ai/docs/output-styles.md +208 -0
- package/skills/ai-build-ai/docs/overview.md +162 -0
- package/skills/ai-build-ai/docs/permissions.md +391 -0
- package/skills/ai-build-ai/docs/plugins.md +396 -0
- package/skills/ai-build-ai/docs/sandbox.md +262 -0
- package/skills/ai-build-ai/scripts/load-tutorial.sh +54 -0
- package/skills/icon-generator/SKILL.md +270 -0
- package/skills/mobile-app-review/SKILL.md +321 -0
- package/skills/mobile-app-review/references/apple-review.md +132 -0
- package/skills/mobile-app-review/references/google-play-review.md +203 -0
- package/skills/mongodb/SKILL.md +667 -0
- package/skills/mongodb/references/mongoose-patterns.md +368 -0
- package/skills/nestjs-architecture/SKILL.md +1086 -0
- package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
- package/skills/performance/SKILL.md +509 -0
- package/skills/react-fsd-architecture/SKILL.md +693 -0
- package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
- package/skills/react-query/SKILL.md +685 -0
- package/skills/react-query/references/query-patterns.md +365 -0
- package/skills/review-code/SKILL.md +321 -0
- package/skills/review-code/references/clean-code-principles.md +395 -0
- package/skills/review-code/references/frontend-patterns.md +136 -0
- package/skills/review-code/references/nestjs-patterns.md +184 -0
- package/skills/review-code/scripts/check-module.sh +201 -0
- package/skills/review-code/scripts/deep-scan.sh +604 -0
- package/skills/review-code/scripts/dep-check.sh +522 -0
- package/skills/review-code/scripts/detect-duplicates.sh +466 -0
- package/skills/review-code/scripts/format-check.sh +577 -0
- package/skills/review-code/scripts/run-review.sh +167 -0
- package/skills/review-code/scripts/scan-codebase.sh +152 -0
- package/skills/security-scanner/SKILL.md +327 -0
- package/skills/security-scanner/references/nestjs-security.md +260 -0
- package/skills/security-scanner/references/nextjs-security.md +201 -0
- package/skills/security-scanner/references/react-native-security.md +199 -0
- package/skills/security-scanner/scripts/security-scan.sh +478 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ─────────────────────────────────────────────────────────────
|
|
3
|
+
# Security Scanner — Automated Checks
|
|
4
|
+
# Aligned with OWASP Top 10:2025 & OWASP LLM Top 10:2025
|
|
5
|
+
#
|
|
6
|
+
# Scans the monorepo for common security issues:
|
|
7
|
+
# - Hardcoded secrets & API keys (A04)
|
|
8
|
+
# - Missing auth guards (A01)
|
|
9
|
+
# - Unsafe patterns: eval, dangerouslySetInnerHTML (A05)
|
|
10
|
+
# - Console logging of sensitive data (A09)
|
|
11
|
+
# - Dependency vulnerabilities & supply chain (A03)
|
|
12
|
+
# - LLM/AI agent risks (LLM01, LLM06)
|
|
13
|
+
# - Exceptional condition handling (A10)
|
|
14
|
+
# - Lockfile integrity & lifecycle scripts
|
|
15
|
+
#
|
|
16
|
+
# Auto-detects backend, frontend, and mobile directories.
|
|
17
|
+
#
|
|
18
|
+
# Usage:
|
|
19
|
+
# ./security-scan.sh all # scan everything
|
|
20
|
+
# ./security-scan.sh backend # backend only
|
|
21
|
+
# ./security-scan.sh frontend # frontend only
|
|
22
|
+
# ./security-scan.sh mobile # mobile only
|
|
23
|
+
# ./security-scan.sh supply-chain # supply chain only
|
|
24
|
+
# ./security-scan.sh llm # LLM/AI security only
|
|
25
|
+
#
|
|
26
|
+
# Environment variables (override auto-detection):
|
|
27
|
+
# BE_DIR=./my-backend FE_DIR=./my-frontend MOBILE_DIR=./my-mobile ./security-scan.sh all
|
|
28
|
+
#
|
|
29
|
+
# Output: JSON report at ./security-output/scan-report.json
|
|
30
|
+
# ─────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
set -euo pipefail
|
|
33
|
+
|
|
34
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
35
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || cd "$SCRIPT_DIR/../../../../.." && pwd)"
|
|
36
|
+
|
|
37
|
+
# ─── Auto-detect project directories ─────────────────────────
|
|
38
|
+
detect_dir() {
|
|
39
|
+
local kind="$1"
|
|
40
|
+
shift
|
|
41
|
+
for candidate in "$@"; do
|
|
42
|
+
if [ -d "$REPO_ROOT/$candidate" ] && [ -f "$REPO_ROOT/$candidate/package.json" ]; then
|
|
43
|
+
echo "$REPO_ROOT/$candidate"
|
|
44
|
+
return
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
echo ""
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
detect_src() {
|
|
51
|
+
local dir="$1"
|
|
52
|
+
if [ -n "$dir" ] && [ -d "$dir/src" ]; then
|
|
53
|
+
echo "$dir/src"
|
|
54
|
+
else
|
|
55
|
+
echo ""
|
|
56
|
+
fi
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
BE_DIR="${BE_DIR:-$(detect_dir backend backend be-* server api apps/api apps/backend)}"
|
|
60
|
+
FE_DIR="${FE_DIR:-$(detect_dir frontend frontend web-* client apps/web apps/frontend)}"
|
|
61
|
+
MOBILE_DIR="${MOBILE_DIR:-$(detect_dir mobile mobile *-mobile apps/mobile)}"
|
|
62
|
+
|
|
63
|
+
BE_SRC="$(detect_src "$BE_DIR")"
|
|
64
|
+
FE_SRC="$(detect_src "$FE_DIR")"
|
|
65
|
+
MOBILE_SRC="$(detect_src "$MOBILE_DIR")"
|
|
66
|
+
|
|
67
|
+
BE_NAME="$([ -n "$BE_DIR" ] && basename "$BE_DIR" || echo "backend")"
|
|
68
|
+
FE_NAME="$([ -n "$FE_DIR" ] && basename "$FE_DIR" || echo "frontend")"
|
|
69
|
+
MOBILE_NAME="$([ -n "$MOBILE_DIR" ] && basename "$MOBILE_DIR" || echo "mobile")"
|
|
70
|
+
|
|
71
|
+
OUTPUT_DIR="$SCRIPT_DIR/../security-output"
|
|
72
|
+
REPORT="$OUTPUT_DIR/scan-report.json"
|
|
73
|
+
|
|
74
|
+
TARGET="${1:-all}"
|
|
75
|
+
|
|
76
|
+
mkdir -p "$OUTPUT_DIR"
|
|
77
|
+
|
|
78
|
+
# ─── Counters ────────────────────────────────────────────────
|
|
79
|
+
CRITICAL=0
|
|
80
|
+
HIGH=0
|
|
81
|
+
MEDIUM=0
|
|
82
|
+
LOW=0
|
|
83
|
+
FINDINGS=()
|
|
84
|
+
|
|
85
|
+
log() { echo " [scan] $*"; }
|
|
86
|
+
finding() {
|
|
87
|
+
local sev="$1" category="$2" file="$3" detail="$4"
|
|
88
|
+
echo " [$sev] $category: $detail"
|
|
89
|
+
echo " → $file"
|
|
90
|
+
FINDINGS+=("{\"severity\":\"$sev\",\"category\":\"$category\",\"file\":\"$file\",\"detail\":$(python3 -c "import json; print(json.dumps('$detail'))" 2>/dev/null || echo "\"$detail\"")}")
|
|
91
|
+
case $sev in
|
|
92
|
+
CRITICAL) CRITICAL=$((CRITICAL + 1)) ;;
|
|
93
|
+
HIGH) HIGH=$((HIGH + 1)) ;;
|
|
94
|
+
MEDIUM) MEDIUM=$((MEDIUM + 1)) ;;
|
|
95
|
+
LOW) LOW=$((LOW + 1)) ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# ─── Scan Functions ──────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
scan_hardcoded_secrets() {
|
|
102
|
+
local dir="$1" label="$2"
|
|
103
|
+
log "Scanning $label for hardcoded secrets..."
|
|
104
|
+
|
|
105
|
+
# API keys / tokens / passwords in source
|
|
106
|
+
while IFS=: read -r file line content; do
|
|
107
|
+
[ -z "$file" ] && continue
|
|
108
|
+
finding "CRITICAL" "HARDCODED_SECRET" "${file#$REPO_ROOT/}:$line" "Possible hardcoded secret"
|
|
109
|
+
done < <(grep -rn \
|
|
110
|
+
-e "api[_-]\?key\s*[:=]\s*['\"][a-zA-Z0-9_-]\{16,\}" \
|
|
111
|
+
-e "password\s*[:=]\s*['\"][^'\"]\{6,\}" \
|
|
112
|
+
-e "secret\s*[:=]\s*['\"][a-zA-Z0-9_-]\{16,\}" \
|
|
113
|
+
-e "Bearer\s\+[a-zA-Z0-9_-]\{20,\}" \
|
|
114
|
+
--include="*.ts" --include="*.tsx" --include="*.mjs" --include="*.js" \
|
|
115
|
+
"$dir" 2>/dev/null | grep -v "node_modules" | grep -v ".env" | grep -v "example" | grep -v "YOUR_" | grep -v "xxx" | head -20 || true)
|
|
116
|
+
|
|
117
|
+
# Private keys
|
|
118
|
+
while IFS=: read -r file line content; do
|
|
119
|
+
[ -z "$file" ] && continue
|
|
120
|
+
finding "CRITICAL" "PRIVATE_KEY" "${file#$REPO_ROOT/}:$line" "Private key found in source"
|
|
121
|
+
done < <(grep -rn "PRIVATE KEY" --include="*.ts" --include="*.tsx" --include="*.mjs" "$dir" 2>/dev/null | grep -v "node_modules" | head -5 || true)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
scan_auth_guards() {
|
|
125
|
+
local dir="$1"
|
|
126
|
+
log "Scanning for missing auth guards..."
|
|
127
|
+
|
|
128
|
+
# Controllers without UseGuards
|
|
129
|
+
while IFS= read -r file; do
|
|
130
|
+
if ! grep -q "UseGuards\|Public\|@Public" "$file" 2>/dev/null; then
|
|
131
|
+
finding "HIGH" "MISSING_AUTH_GUARD" "${file#$REPO_ROOT/}" "Controller has no auth guard"
|
|
132
|
+
fi
|
|
133
|
+
done < <(find "$dir" -name "*.controller.ts" 2>/dev/null | grep -v "node_modules" | grep -v "app.controller" | grep -v "health")
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
scan_injection_risks() {
|
|
137
|
+
local dir="$1" label="$2"
|
|
138
|
+
log "Scanning $label for injection risks..."
|
|
139
|
+
|
|
140
|
+
# eval / Function constructor
|
|
141
|
+
while IFS=: read -r file line content; do
|
|
142
|
+
[ -z "$file" ] && continue
|
|
143
|
+
finding "CRITICAL" "CODE_INJECTION" "${file#$REPO_ROOT/}:$line" "Unsafe eval() or Function() usage"
|
|
144
|
+
done < <(grep -rn "\beval\s*(\|new\s\+Function\s*(" --include="*.ts" --include="*.tsx" --include="*.mjs" "$dir" 2>/dev/null | grep -v "node_modules" | grep -v "eslint" | head -10 || true)
|
|
145
|
+
|
|
146
|
+
# dangerouslySetInnerHTML
|
|
147
|
+
while IFS=: read -r file line content; do
|
|
148
|
+
[ -z "$file" ] && continue
|
|
149
|
+
finding "HIGH" "XSS_RISK" "${file#$REPO_ROOT/}:$line" "dangerouslySetInnerHTML with potential user content"
|
|
150
|
+
done < <(grep -rn "dangerouslySetInnerHTML" --include="*.tsx" --include="*.ts" "$dir" 2>/dev/null | grep -v "node_modules" | head -10 || true)
|
|
151
|
+
|
|
152
|
+
# $where in MongoDB queries
|
|
153
|
+
while IFS=: read -r file line content; do
|
|
154
|
+
[ -z "$file" ] && continue
|
|
155
|
+
finding "CRITICAL" "NOSQL_INJECTION" "${file#$REPO_ROOT/}:$line" "MongoDB \$where usage — potential NoSQL injection"
|
|
156
|
+
done < <(grep -rn '"\$where"\|{.*\$where' --include="*.ts" "$dir" 2>/dev/null | grep -v "node_modules" | head -10 || true)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
scan_sensitive_logging() {
|
|
160
|
+
local dir="$1" label="$2"
|
|
161
|
+
log "Scanning $label for sensitive data in logs..."
|
|
162
|
+
|
|
163
|
+
while IFS=: read -r file line content; do
|
|
164
|
+
[ -z "$file" ] && continue
|
|
165
|
+
finding "MEDIUM" "SENSITIVE_LOG" "${file#$REPO_ROOT/}:$line" "Possible sensitive data in console output"
|
|
166
|
+
done < <(grep -rn \
|
|
167
|
+
-e "console\.\(log\|info\|debug\).*\(token\|password\|secret\|key\|auth\|credential\)" \
|
|
168
|
+
--include="*.ts" --include="*.tsx" --include="*.mjs" \
|
|
169
|
+
"$dir" 2>/dev/null | grep -v "node_modules" | grep -vi "TODO\|FIXME\|example" | head -15 || true)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
scan_unsafe_cors() {
|
|
173
|
+
local dir="$1"
|
|
174
|
+
log "Scanning for unsafe CORS configuration..."
|
|
175
|
+
|
|
176
|
+
while IFS=: read -r file line content; do
|
|
177
|
+
[ -z "$file" ] && continue
|
|
178
|
+
finding "HIGH" "CORS_WILDCARD" "${file#$REPO_ROOT/}:$line" "CORS wildcard (*) configuration found"
|
|
179
|
+
done < <(grep -rn "origin.*\*\|allowedOrigins.*\*\|'*'" --include="*.ts" "$dir" 2>/dev/null | grep -vi "comment\|jsdoc" | grep -v "node_modules" | head -5 || true)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
scan_unsafe_csp() {
|
|
183
|
+
local dir="$1"
|
|
184
|
+
log "Scanning for unsafe CSP directives..."
|
|
185
|
+
|
|
186
|
+
while IFS=: read -r file line content; do
|
|
187
|
+
[ -z "$file" ] && continue
|
|
188
|
+
finding "HIGH" "CSP_UNSAFE_INLINE" "${file#$REPO_ROOT/}:$line" "CSP uses unsafe-inline — XSS protection weakened"
|
|
189
|
+
done < <(grep -rn "unsafe-inline\|unsafe-eval" --include="*.ts" "$dir" 2>/dev/null | grep -v "node_modules" | head -5 || true)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
scan_env_exposure() {
|
|
193
|
+
local dir="$1" label="$2"
|
|
194
|
+
log "Scanning $label for env variable exposure..."
|
|
195
|
+
|
|
196
|
+
# NEXT_PUBLIC_ with sensitive-looking names
|
|
197
|
+
while IFS=: read -r file line content; do
|
|
198
|
+
[ -z "$file" ] && continue
|
|
199
|
+
finding "HIGH" "ENV_EXPOSURE" "${file#$REPO_ROOT/}:$line" "Potentially sensitive env var exposed to client"
|
|
200
|
+
done < <(grep -rn "NEXT_PUBLIC_.*\(SECRET\|KEY\|PASSWORD\|TOKEN\)" --include="*.ts" --include="*.tsx" "$dir" 2>/dev/null | grep -v "node_modules" | head -5 || true)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
scan_deps() {
|
|
204
|
+
local dir="$1" label="$2"
|
|
205
|
+
if [ -f "$dir/package.json" ] && [ -d "$dir/node_modules" ]; then
|
|
206
|
+
log "Auditing $label dependencies..."
|
|
207
|
+
local audit_output
|
|
208
|
+
audit_output=$(cd "$dir" && npm audit --json 2>/dev/null || true)
|
|
209
|
+
local critical_count high_count
|
|
210
|
+
critical_count=$(echo "$audit_output" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('metadata',{}).get('vulnerabilities',{}).get('critical',0))" 2>/dev/null || echo "0")
|
|
211
|
+
high_count=$(echo "$audit_output" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('metadata',{}).get('vulnerabilities',{}).get('high',0))" 2>/dev/null || echo "0")
|
|
212
|
+
if [ "$critical_count" -gt 0 ]; then
|
|
213
|
+
finding "CRITICAL" "DEP_VULNERABILITY" "$label/package.json" "$critical_count critical dependency vulnerabilities"
|
|
214
|
+
fi
|
|
215
|
+
if [ "$high_count" -gt 0 ]; then
|
|
216
|
+
finding "HIGH" "DEP_VULNERABILITY" "$label/package.json" "$high_count high dependency vulnerabilities"
|
|
217
|
+
fi
|
|
218
|
+
else
|
|
219
|
+
log " [skip] $label — node_modules not found"
|
|
220
|
+
fi
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
scan_supply_chain() {
|
|
224
|
+
local dir="$1" label="$2"
|
|
225
|
+
log "Scanning $label supply chain..."
|
|
226
|
+
|
|
227
|
+
# Check lockfile exists and is committed
|
|
228
|
+
if [ -f "$dir/package-lock.json" ] || [ -f "$dir/pnpm-lock.yaml" ] || [ -f "$dir/yarn.lock" ]; then
|
|
229
|
+
log " Lockfile found ✓"
|
|
230
|
+
else
|
|
231
|
+
finding "HIGH" "NO_LOCKFILE" "$label/" "No lockfile found — dependency resolution not deterministic"
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Dangerous lifecycle scripts in dependencies
|
|
235
|
+
if [ -f "$dir/package.json" ]; then
|
|
236
|
+
while IFS= read -r dep_dir; do
|
|
237
|
+
local pkg_json="$dep_dir/package.json"
|
|
238
|
+
[ ! -f "$pkg_json" ] && continue
|
|
239
|
+
if grep -q '"preinstall"\|"postinstall"' "$pkg_json" 2>/dev/null; then
|
|
240
|
+
local pkg_name
|
|
241
|
+
pkg_name=$(python3 -c "import json; print(json.load(open('$pkg_json')).get('name','unknown'))" 2>/dev/null || echo "unknown")
|
|
242
|
+
# Skip well-known packages
|
|
243
|
+
case "$pkg_name" in
|
|
244
|
+
esbuild|sharp|@swc/*|turbo|node-gyp|core-js|husky|cypress) continue ;;
|
|
245
|
+
esac
|
|
246
|
+
finding "MEDIUM" "LIFECYCLE_SCRIPT" "$label/node_modules/$pkg_name" "Package has preinstall/postinstall lifecycle scripts"
|
|
247
|
+
fi
|
|
248
|
+
done < <(find "$dir/node_modules" -maxdepth 2 -name "package.json" -exec dirname {} \; 2>/dev/null | head -50)
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
# Check for @nestjs/devtools-integration CVE-2025-54782
|
|
252
|
+
if [ -f "$dir/node_modules/@nestjs/devtools-integration/package.json" ]; then
|
|
253
|
+
local devtools_ver
|
|
254
|
+
devtools_ver=$(python3 -c "import json; print(json.load(open('$dir/node_modules/@nestjs/devtools-integration/package.json')).get('version','0.0.0'))" 2>/dev/null || echo "0.0.0")
|
|
255
|
+
if python3 -c "from packaging.version import Version; exit(0 if Version('$devtools_ver') <= Version('0.2.0') else 1)" 2>/dev/null; then
|
|
256
|
+
finding "CRITICAL" "CVE_2025_54782" "$label/@nestjs/devtools-integration" "CVE-2025-54782: RCE vulnerability in devtools-integration ≤0.2.0 (current: $devtools_ver)"
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
# npm audit signatures (if npm 9+)
|
|
261
|
+
if [ -d "$dir/node_modules" ]; then
|
|
262
|
+
local sig_output
|
|
263
|
+
sig_output=$(cd "$dir" && npm audit signatures 2>&1 || true)
|
|
264
|
+
if echo "$sig_output" | grep -qi "invalid\|tampered"; then
|
|
265
|
+
finding "CRITICAL" "SIGNATURE_INVALID" "$label/package.json" "npm audit signatures found tampered packages"
|
|
266
|
+
fi
|
|
267
|
+
fi
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
scan_llm_security() {
|
|
271
|
+
local dir="$1"
|
|
272
|
+
log "Scanning for LLM/AI agent security risks..."
|
|
273
|
+
|
|
274
|
+
# Prompt injection vectors — unsanitized user input in prompts
|
|
275
|
+
while IFS=: read -r file line content; do
|
|
276
|
+
[ -z "$file" ] && continue
|
|
277
|
+
finding "HIGH" "PROMPT_INJECTION_RISK" "${file#$REPO_ROOT/}:$line" "User input may be concatenated directly into LLM prompt"
|
|
278
|
+
done < <(grep -rn \
|
|
279
|
+
-e 'prompt.*\${.*\(input\|query\|message\|content\|user\)' \
|
|
280
|
+
-e 'prompt.*\+.*\(input\|query\|message\|content\|user\)' \
|
|
281
|
+
-e 'template.*\${.*\(input\|query\|message\|content\)' \
|
|
282
|
+
--include="*.ts" --include="*.mjs" \
|
|
283
|
+
"$dir" 2>/dev/null | grep -v "node_modules" | grep -v "test" | grep -v "spec" | head -15 || true)
|
|
284
|
+
|
|
285
|
+
# System prompt exposure
|
|
286
|
+
while IFS=: read -r file line content; do
|
|
287
|
+
[ -z "$file" ] && continue
|
|
288
|
+
finding "MEDIUM" "SYSTEM_PROMPT_EXPOSURE" "${file#$REPO_ROOT/}:$line" "System prompt may be logged or returned in response"
|
|
289
|
+
done < <(grep -rn \
|
|
290
|
+
-e 'console\.log.*system.*prompt\|console\.log.*systemMessage' \
|
|
291
|
+
-e 'response.*system.*prompt\|return.*system.*prompt' \
|
|
292
|
+
--include="*.ts" \
|
|
293
|
+
"$dir" 2>/dev/null | grep -v "node_modules" | head -5 || true)
|
|
294
|
+
|
|
295
|
+
# Excessive agency — tools without permission checks
|
|
296
|
+
while IFS=: read -r file line content; do
|
|
297
|
+
[ -z "$file" ] && continue
|
|
298
|
+
finding "MEDIUM" "EXCESSIVE_AGENCY" "${file#$REPO_ROOT/}:$line" "LangGraph tool definition — verify permission scoping"
|
|
299
|
+
done < <(grep -rn \
|
|
300
|
+
-e '@tool\|new DynamicTool\|StructuredTool\|createTool' \
|
|
301
|
+
--include="*.ts" \
|
|
302
|
+
"$dir" 2>/dev/null | grep -v "node_modules" | head -10 || true)
|
|
303
|
+
|
|
304
|
+
# Unbounded LLM consumption — missing token limits
|
|
305
|
+
while IFS=: read -r file line content; do
|
|
306
|
+
[ -z "$file" ] && continue
|
|
307
|
+
if ! grep -q "maxTokens\|max_tokens\|maxOutputTokens" "$file" 2>/dev/null; then
|
|
308
|
+
finding "MEDIUM" "UNBOUNDED_LLM" "${file#$REPO_ROOT/}:$line" "LLM call without explicit token limit"
|
|
309
|
+
fi
|
|
310
|
+
done < <(grep -rln \
|
|
311
|
+
-e 'invoke\|generate\|chat\|complete' \
|
|
312
|
+
--include="*.ts" \
|
|
313
|
+
"$dir/features/ai-agents" "$dir/features/llm" 2>/dev/null | head -10 || true)
|
|
314
|
+
|
|
315
|
+
# Unsanitized AI output rendering — scan frontend and mobile if available
|
|
316
|
+
local ai_render_dirs=()
|
|
317
|
+
[ -n "$FE_SRC" ] && [ -d "$FE_SRC" ] && ai_render_dirs+=("$FE_SRC")
|
|
318
|
+
[ -n "$MOBILE_SRC" ] && [ -d "$MOBILE_SRC" ] && ai_render_dirs+=("$MOBILE_SRC")
|
|
319
|
+
if [ ${#ai_render_dirs[@]} -gt 0 ]; then
|
|
320
|
+
while IFS=: read -r file line content; do
|
|
321
|
+
[ -z "$file" ] && continue
|
|
322
|
+
finding "HIGH" "UNSANITIZED_AI_OUTPUT" "${file#$REPO_ROOT/}:$line" "AI-generated content rendered with dangerouslySetInnerHTML"
|
|
323
|
+
done < <(grep -rn "dangerouslySetInnerHTML" --include="*.tsx" "${ai_render_dirs[@]}" 2>/dev/null | grep -i "ai\|chat\|message\|response\|assistant" | grep -v "node_modules" | head -5 || true)
|
|
324
|
+
fi
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
scan_error_handling() {
|
|
328
|
+
local dir="$1" label="$2"
|
|
329
|
+
log "Scanning $label for exceptional condition handling (A10:2025)..."
|
|
330
|
+
|
|
331
|
+
# Fail-open patterns — catch blocks that don't re-throw or handle
|
|
332
|
+
while IFS=: read -r file line content; do
|
|
333
|
+
[ -z "$file" ] && continue
|
|
334
|
+
finding "MEDIUM" "FAIL_OPEN" "${file#$REPO_ROOT/}:$line" "Empty or comment-only catch block — may fail open"
|
|
335
|
+
done < <(grep -rn -A1 "catch\s*(" --include="*.ts" --include="*.tsx" "$dir" 2>/dev/null | grep -B1 "^\s*}\|^\s*//\|^\s*$" | grep "catch" | grep -v "node_modules" | head -10 || true)
|
|
336
|
+
|
|
337
|
+
# Stack trace exposure in production
|
|
338
|
+
while IFS=: read -r file line content; do
|
|
339
|
+
[ -z "$file" ] && continue
|
|
340
|
+
finding "MEDIUM" "STACK_EXPOSURE" "${file#$REPO_ROOT/}:$line" "Error stack trace may be exposed in response"
|
|
341
|
+
done < <(grep -rn "error\.\(stack\|message\)" --include="*.ts" "$dir" 2>/dev/null | grep -i "response\|send\|json\|throw.*Http" | grep -v "node_modules" | grep -v "logger\|log\.\|pino" | head -10 || true)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
# ─── Execute Scans ───────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
echo ""
|
|
347
|
+
echo " ╔══════════════════════════════════════════╗"
|
|
348
|
+
echo " ║ Security Scanner ║"
|
|
349
|
+
echo " ║ Target: $TARGET"
|
|
350
|
+
echo " ╚══════════════════════════════════════════╝"
|
|
351
|
+
echo ""
|
|
352
|
+
|
|
353
|
+
if [ -n "$BE_DIR" ]; then
|
|
354
|
+
log "Detected backend: $BE_NAME"
|
|
355
|
+
fi
|
|
356
|
+
if [ -n "$FE_DIR" ]; then
|
|
357
|
+
log "Detected frontend: $FE_NAME"
|
|
358
|
+
fi
|
|
359
|
+
if [ -n "$MOBILE_DIR" ]; then
|
|
360
|
+
log "Detected mobile: $MOBILE_NAME"
|
|
361
|
+
fi
|
|
362
|
+
echo ""
|
|
363
|
+
|
|
364
|
+
if [[ "$TARGET" == "backend" || "$TARGET" == "all" ]]; then
|
|
365
|
+
log ""
|
|
366
|
+
if [ -z "$BE_SRC" ]; then
|
|
367
|
+
log "[skip] No backend source directory detected — set BE_DIR env var"
|
|
368
|
+
else
|
|
369
|
+
log "═══ Backend ($BE_NAME) ═══"
|
|
370
|
+
scan_hardcoded_secrets "$BE_SRC" "backend"
|
|
371
|
+
scan_auth_guards "$BE_SRC"
|
|
372
|
+
scan_injection_risks "$BE_SRC" "backend"
|
|
373
|
+
scan_sensitive_logging "$BE_SRC" "backend"
|
|
374
|
+
scan_unsafe_cors "$BE_SRC"
|
|
375
|
+
scan_unsafe_csp "$BE_SRC"
|
|
376
|
+
scan_error_handling "$BE_SRC" "backend"
|
|
377
|
+
scan_deps "$BE_DIR" "$BE_NAME"
|
|
378
|
+
scan_supply_chain "$BE_DIR" "$BE_NAME"
|
|
379
|
+
fi
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
if [[ "$TARGET" == "frontend" || "$TARGET" == "all" ]]; then
|
|
383
|
+
log ""
|
|
384
|
+
if [ -z "$FE_SRC" ]; then
|
|
385
|
+
log "[skip] No frontend source directory detected — set FE_DIR env var"
|
|
386
|
+
else
|
|
387
|
+
log "═══ Frontend ($FE_NAME) ═══"
|
|
388
|
+
scan_hardcoded_secrets "$FE_SRC" "frontend"
|
|
389
|
+
scan_injection_risks "$FE_SRC" "frontend"
|
|
390
|
+
scan_sensitive_logging "$FE_SRC" "frontend"
|
|
391
|
+
scan_env_exposure "$FE_SRC" "frontend"
|
|
392
|
+
scan_error_handling "$FE_SRC" "frontend"
|
|
393
|
+
scan_deps "$FE_DIR" "$FE_NAME"
|
|
394
|
+
scan_supply_chain "$FE_DIR" "$FE_NAME"
|
|
395
|
+
fi
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
if [[ "$TARGET" == "mobile" || "$TARGET" == "all" ]]; then
|
|
399
|
+
log ""
|
|
400
|
+
if [ -z "$MOBILE_SRC" ] || [ ! -d "$MOBILE_SRC" ]; then
|
|
401
|
+
log "[skip] No mobile source directory detected — set MOBILE_DIR env var"
|
|
402
|
+
else
|
|
403
|
+
log "═══ Mobile ($MOBILE_NAME) ═══"
|
|
404
|
+
scan_hardcoded_secrets "$MOBILE_SRC" "mobile"
|
|
405
|
+
scan_injection_risks "$MOBILE_SRC" "mobile"
|
|
406
|
+
scan_sensitive_logging "$MOBILE_SRC" "mobile"
|
|
407
|
+
scan_error_handling "$MOBILE_SRC" "mobile"
|
|
408
|
+
scan_deps "$MOBILE_DIR" "$MOBILE_NAME"
|
|
409
|
+
scan_supply_chain "$MOBILE_DIR" "$MOBILE_NAME"
|
|
410
|
+
fi
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
if [[ "$TARGET" == "supply-chain" || "$TARGET" == "all" ]]; then
|
|
414
|
+
log ""
|
|
415
|
+
log "═══ Supply Chain Analysis ═══"
|
|
416
|
+
[ -n "$BE_DIR" ] && scan_supply_chain "$BE_DIR" "$BE_NAME"
|
|
417
|
+
[ -n "$FE_DIR" ] && scan_supply_chain "$FE_DIR" "$FE_NAME"
|
|
418
|
+
[ -n "$MOBILE_DIR" ] && scan_supply_chain "$MOBILE_DIR" "$MOBILE_NAME"
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
if [[ "$TARGET" == "llm" || "$TARGET" == "all" ]]; then
|
|
422
|
+
log ""
|
|
423
|
+
log "═══ LLM/AI Agent Security (OWASP LLM Top 10) ═══"
|
|
424
|
+
if [ -n "$BE_SRC" ] && [ -d "$BE_SRC" ]; then
|
|
425
|
+
scan_llm_security "$BE_SRC"
|
|
426
|
+
else
|
|
427
|
+
log "[skip] No backend source directory for LLM scan"
|
|
428
|
+
fi
|
|
429
|
+
fi
|
|
430
|
+
|
|
431
|
+
# ─── Generate Report ─────────────────────────────────────────
|
|
432
|
+
log ""
|
|
433
|
+
log "═══ Generating Report ═══"
|
|
434
|
+
|
|
435
|
+
FINDINGS_JSON=$(printf '%s\n' "${FINDINGS[@]}" | paste -sd ',' - 2>/dev/null || echo "")
|
|
436
|
+
cat > "$REPORT" <<EOF
|
|
437
|
+
{
|
|
438
|
+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
439
|
+
"target": "$TARGET",
|
|
440
|
+
"summary": {
|
|
441
|
+
"critical": $CRITICAL,
|
|
442
|
+
"high": $HIGH,
|
|
443
|
+
"medium": $MEDIUM,
|
|
444
|
+
"low": $LOW,
|
|
445
|
+
"total": $((CRITICAL + HIGH + MEDIUM + LOW))
|
|
446
|
+
},
|
|
447
|
+
"findings": [${FINDINGS_JSON}]
|
|
448
|
+
}
|
|
449
|
+
EOF
|
|
450
|
+
|
|
451
|
+
log "Report saved to $REPORT"
|
|
452
|
+
|
|
453
|
+
# ─── Summary ─────────────────────────────────────────────────
|
|
454
|
+
TOTAL=$((CRITICAL + HIGH + MEDIUM + LOW))
|
|
455
|
+
|
|
456
|
+
echo ""
|
|
457
|
+
echo " ════════════════════════════════════════════"
|
|
458
|
+
echo " Security Scan Results"
|
|
459
|
+
echo " ────────────────────"
|
|
460
|
+
echo " 🔴 Critical: $CRITICAL"
|
|
461
|
+
echo " 🟠 High: $HIGH"
|
|
462
|
+
echo " 🟡 Medium: $MEDIUM"
|
|
463
|
+
echo " 🟢 Low: $LOW"
|
|
464
|
+
echo " Total: $TOTAL findings"
|
|
465
|
+
echo " ════════════════════════════════════════════"
|
|
466
|
+
|
|
467
|
+
if [ $CRITICAL -gt 0 ]; then
|
|
468
|
+
echo ""
|
|
469
|
+
echo " ⚠️ CRITICAL issues found — fix immediately!"
|
|
470
|
+
exit 2
|
|
471
|
+
elif [ $HIGH -gt 0 ]; then
|
|
472
|
+
echo ""
|
|
473
|
+
echo " ⚠️ HIGH issues found — fix before next release"
|
|
474
|
+
exit 1
|
|
475
|
+
else
|
|
476
|
+
echo ""
|
|
477
|
+
echo " ✅ No critical/high issues found"
|
|
478
|
+
fi
|