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.
Files changed (92) hide show
  1. package/README.md +77 -0
  2. package/agents/developer.md +56 -0
  3. package/agents/frontend-design.md +69 -0
  4. package/agents/mobile-reviewer.md +36 -0
  5. package/agents/review-code.md +49 -0
  6. package/agents/security-scanner.md +50 -0
  7. package/agents/tester.md +72 -0
  8. package/bin/cli.js +226 -0
  9. package/package.json +20 -0
  10. package/skills/ai-asset-generator/SKILL.md +255 -0
  11. package/skills/ai-asset-generator/docs/gen-image.md +274 -0
  12. package/skills/ai-asset-generator/docs/genvideo.md +341 -0
  13. package/skills/ai-asset-generator/docs/remove-background.md +19 -0
  14. package/skills/ai-asset-generator/generate-credit-assets.mjs +180 -0
  15. package/skills/ai-asset-generator/generate-ginbrowser-assets.mjs +242 -0
  16. package/skills/ai-asset-generator/generate-sty-icon.mjs +149 -0
  17. package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
  18. package/skills/ai-asset-generator/lib/env.mjs +38 -0
  19. package/skills/ai-asset-generator/lib/kie-client.mjs +88 -0
  20. package/skills/ai-asset-generator/scripts/scaffold-generator.mjs +203 -0
  21. package/skills/ai-build-ai/SKILL.md +124 -0
  22. package/skills/ai-build-ai/docs/agent-teams.md +293 -0
  23. package/skills/ai-build-ai/docs/checkpointing.md +161 -0
  24. package/skills/ai-build-ai/docs/create-agent.md +399 -0
  25. package/skills/ai-build-ai/docs/create-mcp.md +395 -0
  26. package/skills/ai-build-ai/docs/create-skill.md +299 -0
  27. package/skills/ai-build-ai/docs/headless-mode.md +614 -0
  28. package/skills/ai-build-ai/docs/hooks.md +578 -0
  29. package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
  30. package/skills/ai-build-ai/docs/output-styles.md +208 -0
  31. package/skills/ai-build-ai/docs/overview.md +162 -0
  32. package/skills/ai-build-ai/docs/permissions.md +391 -0
  33. package/skills/ai-build-ai/docs/plugins.md +396 -0
  34. package/skills/ai-build-ai/docs/sandbox.md +262 -0
  35. package/skills/ai-build-ai/scripts/load-tutorial.sh +54 -0
  36. package/skills/icon-generator/SKILL.md +270 -0
  37. package/skills/mobile-app-review/SKILL.md +321 -0
  38. package/skills/mobile-app-review/references/apple-review.md +132 -0
  39. package/skills/mobile-app-review/references/google-play-review.md +203 -0
  40. package/skills/mongodb/SKILL.md +667 -0
  41. package/skills/mongodb/references/mongoose-patterns.md +368 -0
  42. package/skills/nestjs-architecture/SKILL.md +1086 -0
  43. package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
  44. package/skills/performance/SKILL.md +509 -0
  45. package/skills/react-fsd-architecture/SKILL.md +693 -0
  46. package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
  47. package/skills/react-query/SKILL.md +685 -0
  48. package/skills/react-query/references/query-patterns.md +365 -0
  49. package/skills/review-code/SKILL.md +321 -0
  50. package/skills/review-code/references/clean-code-principles.md +395 -0
  51. package/skills/review-code/references/frontend-patterns.md +136 -0
  52. package/skills/review-code/references/nestjs-patterns.md +184 -0
  53. package/skills/review-code/scripts/check-module.sh +201 -0
  54. package/skills/review-code/scripts/deep-scan.sh +604 -0
  55. package/skills/review-code/scripts/dep-check.sh +522 -0
  56. package/skills/review-code/scripts/detect-duplicates.sh +466 -0
  57. package/skills/review-code/scripts/format-check.sh +577 -0
  58. package/skills/review-code/scripts/run-review.sh +167 -0
  59. package/skills/review-code/scripts/scan-codebase.sh +152 -0
  60. package/skills/security-scanner/SKILL.md +327 -0
  61. package/skills/security-scanner/references/nestjs-security.md +260 -0
  62. package/skills/security-scanner/references/nextjs-security.md +201 -0
  63. package/skills/security-scanner/references/react-native-security.md +199 -0
  64. package/skills/security-scanner/scripts/security-scan.sh +478 -0
  65. package/skills/ui-ux-pro-max/SKILL.md +377 -0
  66. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  67. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  68. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  69. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  70. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  71. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  72. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  73. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  74. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  75. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  76. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  77. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  78. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  79. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  80. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  81. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  82. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  83. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  84. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  85. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  86. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  87. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  88. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  89. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  90. package/skills/ui-ux-pro-max/scripts/core.py +253 -0
  91. package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  92. 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