laravel-security-agent 1.2.1 → 1.3.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 +49 -42
- package/bin/index.js +64 -29
- package/package.json +3 -3
- package/src/deploy-claude-skill.js +28 -0
- package/src/deploy-gemini.js +48 -0
- package/src/update-gitignore.js +34 -5
- package/templates/claude-skill/SKILL.md +88 -0
- package/templates/gemini.md +26 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
> *"Your friendly capybara keeping your Laravel app safe."*
|
|
6
6
|
|
|
7
|
-
A one-command security toolkit for Laravel projects. Capi Guard
|
|
7
|
+
A one-command security toolkit for Laravel projects. Run it once and Capi Guard automatically deploys AI-powered security agents into **Claude Code**, **GitHub Copilot**, and **Google Gemini/Antigravity** — plus hardens your project with a pre-commit hook and `.gitignore` rules.
|
|
8
8
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
|
@@ -14,46 +14,55 @@ Run at the root of your Laravel project:
|
|
|
14
14
|
npx laravel-security-agent
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
No prompts. Everything installs automatically.
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## What Gets Installed
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
### Global AI Agents (user-level, one-time)
|
|
22
22
|
|
|
23
|
-
|
|
|
24
|
-
|
|
25
|
-
| **Claude Code
|
|
26
|
-
| **Copilot
|
|
27
|
-
| **
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
| Platform | What installs | Where |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| **Claude Code** | `capi-guard` skill | `~/.claude/skills/capi-guard/SKILL.md` |
|
|
26
|
+
| **GitHub Copilot** | `.agent.md` sub-agents | `.github/agents/` |
|
|
27
|
+
| **Gemini / Google Antigravity** | Capi Guard instruction block | `~/.gemini/GEMINI.md` |
|
|
28
|
+
|
|
29
|
+
After install, just ask your AI to "audit security" — it knows the full workflow and all 4 callable skills.
|
|
30
|
+
|
|
31
|
+
### Project Files
|
|
32
|
+
|
|
33
|
+
| File | Purpose |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `SECURITY.md` | Claude Code security agent rules |
|
|
36
|
+
| `.github/copilot-instructions.md` | Copilot security agent rules |
|
|
37
|
+
| `.gitignore` entries | Blocks `deploy.php`, `.env`, SSH keys |
|
|
38
|
+
| `.git/hooks/pre-commit` | Blocks commits of sensitive files |
|
|
39
|
+
| **Antigravity PHP Agent** | Full autonomous agent in `app/Agents/` |
|
|
30
40
|
|
|
31
41
|
---
|
|
32
42
|
|
|
33
43
|
## 🤖 The Antigravity Agent (PHP)
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
It exposes an API endpoint (`/api/agent/invoke`) that GitHub Copilot can call to perform **deterministic, local code analysis** natively in PHP.
|
|
45
|
+
The flagship feature. Capi Guard scaffolds a production-ready, zero-trust AI agent directly into your Laravel `app/` directory. It exposes `/api/agent/invoke` — an endpoint that any AI can call to run **deterministic, local PHP analysis** without sending your codebase to the cloud.
|
|
38
46
|
|
|
39
47
|
### Available Skills
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
| Skill | What it does |
|
|
50
|
+
|---|---|
|
|
51
|
+
| 🔍 **vulnerabilityScan** | Regex scan across 13 security categories in any path |
|
|
52
|
+
| 🛡️ **analyzeAuthFlow** | PHP Reflection traces controllers, finds missing `authorize()` calls |
|
|
53
|
+
| 🩹 **applySecurityPatch** | Applies CVE patches with backup + runs Artisan commands post-patch |
|
|
54
|
+
| 🧹 **sanitizeGitHistory** | Generates a `git filter-repo` script to purge secrets from full git history |
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
2. 🛡️ **analyzeAuthFlow**: Uses PHP Reflection to trace controllers and detect missing `$this->authorize()` calls.
|
|
45
|
-
3. 🩹 **applySecurityPatch**: Applies surgical CVE patches and automatically runs Artisan commands afterwards (`optimize:clear`, `test`, etc).
|
|
46
|
-
4. 🧹 **sanitizeGitHistory**: Generates a `git filter-repo` script with a blob-callback to purge old `.env` files and redact naked passwords/IPs from your *entire* Git history commits.
|
|
56
|
+
### Copilot Sub-Agents
|
|
47
57
|
|
|
48
|
-
|
|
58
|
+
Four specialised agents are installed into `.github/agents/`:
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
- `@capi-guard` — Full-stack auditor. Runs scans, analyzes auth, patches files.
|
|
61
|
+
- `@patch-aplicado` — Surgical CVE patcher with backup and Artisan validation.
|
|
62
|
+
- `@auth-guard` — Read-only analyst. Finds authorization gaps and suggests Policies.
|
|
63
|
+
- `@git-sanitizer` — Git history auditor and scrubber.
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
- `@patch-aplicado` — Surgical CVE patcher. Always creates a backup, runs the patch, and validates with Artisan.
|
|
54
|
-
- `@auth-guard` — Read-only analyst. Only reads code to find authorization gaps and suggest Policies.
|
|
55
|
-
|
|
56
|
-
### How to configure the Agent
|
|
65
|
+
### Agent Setup
|
|
57
66
|
|
|
58
67
|
1. Add the route to `routes/api.php`:
|
|
59
68
|
```php
|
|
@@ -63,40 +72,38 @@ The installer also copies 3 specialised Copilot Agents into `.github/agents/`. Y
|
|
|
63
72
|
Route::post("/api/agent/invoke", AgentController::class)
|
|
64
73
|
->middleware(["auth:sanctum", ZeroTrustMiddleware::class]);
|
|
65
74
|
```
|
|
66
|
-
2. Issue a Sanctum token with the `agent:invoke` ability
|
|
75
|
+
2. Issue a Sanctum token with the `agent:invoke` ability.
|
|
67
76
|
3. Register `.github/manifest.json` in your GitHub Copilot Extension settings.
|
|
68
|
-
4. *(Optional)* Publish the config
|
|
77
|
+
4. *(Optional)* Publish the config to customize CVE patches and rate limits:
|
|
69
78
|
```bash
|
|
70
79
|
php artisan vendor:publish --tag=security-agent-config
|
|
71
80
|
```
|
|
72
81
|
|
|
73
82
|
---
|
|
74
83
|
|
|
75
|
-
##
|
|
76
|
-
|
|
77
|
-
If you just want instructions for your AI without the full PHP agent, install `SECURITY.md` or `.github/copilot-instructions.md`.
|
|
84
|
+
## Security Categories Audited
|
|
78
85
|
|
|
79
|
-
When you ask
|
|
86
|
+
When you ask any AI to "audit security", it checks 13 categories:
|
|
80
87
|
|
|
81
88
|
| Category | What gets checked |
|
|
82
|
-
|
|
89
|
+
|---|---|
|
|
83
90
|
| **IDOR** | Policies, `authorize()`, ownership scoping |
|
|
84
91
|
| **SQL Injection** | Raw queries, dynamic `orderBy` whitelisting |
|
|
85
92
|
| **Mass Assignment** | Empty `$guarded`, role escalation via `$request->all()` |
|
|
93
|
+
| **XSS** | `{!! !!}` in Blade, `v-html` in Vue |
|
|
94
|
+
| **CSRF** | Exclusions in `VerifyCsrfToken`, unprotected POST routes |
|
|
95
|
+
| **Authorization** | Missing `Gate::authorize()` in controllers |
|
|
86
96
|
| **File Uploads** | `mimetypes:` vs `mimes:`, private storage, server-side naming |
|
|
87
|
-
| **
|
|
88
|
-
| **
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
The `.gitignore` updater enforces patterns for `deploy.php`, `auth.json`, e `.phpunit.result.cache`.
|
|
93
|
-
The pre-commit hook automatically blocks attempts to `git commit` any `.env` file, `.pem`, `.p12`, or `.key` file.
|
|
97
|
+
| **Rate Limiting** | Public endpoints without throttle middleware |
|
|
98
|
+
| **Security Headers** | Missing CSP, X-Frame-Options, HSTS |
|
|
99
|
+
| **Credentials in Code** | Hardcoded secrets and API keys |
|
|
100
|
+
| **Git Secrets** | `.env`, `deploy.php`, SSH keys in git history |
|
|
94
101
|
|
|
95
102
|
## Requirements
|
|
96
103
|
|
|
97
|
-
- Node.js 18+
|
|
104
|
+
- Node.js 18+
|
|
98
105
|
- Laravel project (`composer.json` at the root)
|
|
99
|
-
- *For the Agent:* PHP 8.2+ and Laravel Sanctum
|
|
106
|
+
- *For the PHP Agent:* PHP 8.2+ and Laravel Sanctum
|
|
100
107
|
|
|
101
108
|
## License
|
|
102
109
|
|
package/bin/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
intro, outro,
|
|
3
|
+
intro, outro, confirm,
|
|
4
4
|
spinner, note, cancel, isCancel
|
|
5
5
|
} from '@clack/prompts';
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
6
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
8
9
|
import { copySecurity } from '../src/copy-security.js';
|
|
9
10
|
import { copyCopilot } from '../src/copy-copilot.js';
|
|
10
11
|
import { applyGitignore } from '../src/update-gitignore.js';
|
|
11
12
|
import { installHook } from '../src/install-hook.js';
|
|
12
13
|
import { copyAgent } from '../src/copy-agent.js';
|
|
14
|
+
import { deployClaudeSkill } from '../src/deploy-claude-skill.js';
|
|
15
|
+
import { deployGemini } from '../src/deploy-gemini.js';
|
|
13
16
|
|
|
14
17
|
const cwd = process.cwd();
|
|
15
18
|
|
|
@@ -23,22 +26,7 @@ if (!existsSync(join(cwd, 'composer.json'))) {
|
|
|
23
26
|
);
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
const options =
|
|
27
|
-
message: 'What would you like to install?',
|
|
28
|
-
initialValues: ['claude', 'copilot', 'gitignore', 'hook'],
|
|
29
|
-
options: [
|
|
30
|
-
{ value: 'claude', label: 'SECURITY.md', hint: 'Claude Code security agent' },
|
|
31
|
-
{ value: 'copilot', label: '.github/copilot-instructions.md', hint: 'GitHub Copilot security agent' },
|
|
32
|
-
{ value: 'gitignore',label: 'Update .gitignore', hint: 'protects deploy.php, .env, SSH keys' },
|
|
33
|
-
{ value: 'hook', label: 'Pre-commit hook', hint: 'blocks commits of sensitive files' },
|
|
34
|
-
{ value: 'agent', label: 'Antigravity Agent (PHP)', hint: 'Copilot Skills + PHP 8.3 agent classes' },
|
|
35
|
-
],
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
if (isCancel(options)) {
|
|
39
|
-
cancel('Installation cancelled.');
|
|
40
|
-
process.exit(0);
|
|
41
|
-
}
|
|
29
|
+
const options = ['claude', 'copilot', 'gitignore', 'hook', 'agent'];
|
|
42
30
|
|
|
43
31
|
const s = spinner();
|
|
44
32
|
|
|
@@ -102,6 +90,48 @@ if (options.includes('hook')) {
|
|
|
102
90
|
: '✔ pre-commit hook installed at .git/hooks/pre-commit');
|
|
103
91
|
}
|
|
104
92
|
|
|
93
|
+
// --- Claude Code skill ---
|
|
94
|
+
{
|
|
95
|
+
const dest = join(homedir(), '.claude', 'skills', 'capi-guard', 'SKILL.md');
|
|
96
|
+
let overwrite = false;
|
|
97
|
+
|
|
98
|
+
if (existsSync(dest)) {
|
|
99
|
+
const answer = await confirm({
|
|
100
|
+
message: '~/.claude/skills/capi-guard/SKILL.md already exists. Overwrite?',
|
|
101
|
+
initialValue: false,
|
|
102
|
+
});
|
|
103
|
+
if (isCancel(answer)) { cancel('Cancelled.'); process.exit(0); }
|
|
104
|
+
overwrite = answer;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
s.start('Deploying Capi Guard skill to Claude Code...');
|
|
108
|
+
const result = deployClaudeSkill(undefined, overwrite);
|
|
109
|
+
s.stop(result.skipped
|
|
110
|
+
? '~/.claude/skills/capi-guard/SKILL.md kept (not overwritten)'
|
|
111
|
+
: '✔ Capi Guard skill installed at ~/.claude/skills/capi-guard/SKILL.md');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// --- Gemini / Google Antigravity ---
|
|
115
|
+
{
|
|
116
|
+
const dest = join(homedir(), '.gemini', 'GEMINI.md');
|
|
117
|
+
let overwrite = false;
|
|
118
|
+
|
|
119
|
+
if (existsSync(dest) && readFileSync(dest, 'utf8').includes('<!-- capi-guard -->')) {
|
|
120
|
+
const answer = await confirm({
|
|
121
|
+
message: '~/.gemini/GEMINI.md already has Capi Guard block. Overwrite?',
|
|
122
|
+
initialValue: false,
|
|
123
|
+
});
|
|
124
|
+
if (isCancel(answer)) { cancel('Cancelled.'); process.exit(0); }
|
|
125
|
+
overwrite = answer;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
s.start('Deploying Capi Guard to Gemini / Google Antigravity...');
|
|
129
|
+
const result = deployGemini(undefined, overwrite);
|
|
130
|
+
s.stop(result.skipped
|
|
131
|
+
? '~/.gemini/GEMINI.md kept (not overwritten)'
|
|
132
|
+
: '✔ Capi Guard block appended to ~/.gemini/GEMINI.md');
|
|
133
|
+
}
|
|
134
|
+
|
|
105
135
|
// --- Antigravity Agent (PHP) ---
|
|
106
136
|
if (options.includes('agent')) {
|
|
107
137
|
let overwrite = false;
|
|
@@ -123,19 +153,24 @@ if (options.includes('agent')) {
|
|
|
123
153
|
: `✔ Antigravity agent installed (${result.copied.length} files → app/Agents/, app/Skills/, app/Http/, config/, .github/)`);
|
|
124
154
|
}
|
|
125
155
|
|
|
126
|
-
const nextSteps = [
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
156
|
+
const nextSteps = [
|
|
157
|
+
'Claude Code: the "capi-guard" skill is now available — just ask Claude to audit security',
|
|
158
|
+
'Gemini CLI: Capi Guard instructions are active in ~/.gemini/GEMINI.md',
|
|
159
|
+
'Copilot: .github/copilot-instructions.md and .github/agents/ are installed automatically',
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
if (options.includes('agent')) {
|
|
163
|
+
nextSteps.push(
|
|
164
|
+
'Antigravity Agent (PHP backend):\n' +
|
|
165
|
+
' 1. Add route: Route::post("/api/agent/invoke", AgentController::class)->middleware(["auth:sanctum", ZeroTrustMiddleware::class]);\n' +
|
|
166
|
+
' 2. Issue a Sanctum token with ability "agent:invoke"\n' +
|
|
167
|
+
' 3. Register .github/manifest.json in your Copilot Extension settings\n' +
|
|
168
|
+
' 4. Optionally run: php artisan vendor:publish --tag=security-agent-config'
|
|
169
|
+
);
|
|
170
|
+
}
|
|
136
171
|
|
|
137
172
|
if (nextSteps.length > 0) {
|
|
138
|
-
note(nextSteps.join('\n'), 'Next
|
|
173
|
+
note(nextSteps.join('\n'), 'Next steps');
|
|
139
174
|
}
|
|
140
175
|
|
|
141
176
|
const capybara = `
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "laravel-security-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Capi Guard — a security audit agent for Laravel projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"public"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"test": "node --test test/copy-security.test.js test/update-gitignore.test.js test/install-hook.test.js test/copy-copilot.test.js test/copy-agent.test.js",
|
|
16
|
+
"test": "node --test test/copy-security.test.js test/update-gitignore.test.js test/install-hook.test.js test/copy-copilot.test.js test/copy-agent.test.js test/deploy-claude-skill.test.js test/deploy-gemini.test.js",
|
|
17
17
|
"start": "node bin/index.js",
|
|
18
|
-
"prepublishOnly": "node --test test/copy-security.test.js test/update-gitignore.test.js test/install-hook.test.js test/copy-copilot.test.js test/copy-agent.test.js"
|
|
18
|
+
"prepublishOnly": "node --test test/copy-security.test.js test/update-gitignore.test.js test/install-hook.test.js test/copy-copilot.test.js test/copy-agent.test.js test/deploy-claude-skill.test.js test/deploy-gemini.test.js"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@clack/prompts": "^0.9.1"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/deploy-claude-skill.js
|
|
2
|
+
import { existsSync, copyFileSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
|
|
7
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
8
|
+
const SKILL_TEMPLATE = join(__dirname, '../templates/claude-skill/SKILL.md');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Deploy the Capi Guard skill to the user's Claude Code skills directory.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} [homeDir] Override home directory (for testing).
|
|
14
|
+
* @param {boolean} [overwrite] Whether to overwrite an existing skill file.
|
|
15
|
+
* @returns {{ skipped: boolean, path: string }}
|
|
16
|
+
*/
|
|
17
|
+
export function deployClaudeSkill(homeDir = homedir(), overwrite = false) {
|
|
18
|
+
const skillDir = join(homeDir, '.claude', 'skills', 'capi-guard');
|
|
19
|
+
const dest = join(skillDir, 'SKILL.md');
|
|
20
|
+
|
|
21
|
+
if (existsSync(dest) && !overwrite) {
|
|
22
|
+
return { skipped: true, path: dest };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
mkdirSync(skillDir, { recursive: true });
|
|
26
|
+
copyFileSync(SKILL_TEMPLATE, dest);
|
|
27
|
+
return { skipped: false, path: dest };
|
|
28
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/deploy-gemini.js
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
|
|
7
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
8
|
+
const GEMINI_TEMPLATE = join(__dirname, '../templates/gemini.md');
|
|
9
|
+
|
|
10
|
+
const OPEN_MARKER = '<!-- capi-guard -->';
|
|
11
|
+
const CLOSE_MARKER = '<!-- /capi-guard -->';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Append (or re-append) the Capi Guard block to the user's ~/.gemini/GEMINI.md.
|
|
15
|
+
*
|
|
16
|
+
* Idempotent: if the marker is already present and overwrite is false, skip.
|
|
17
|
+
* When overwrite is true, the old block is stripped and the fresh template is appended.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} [homeDir] Override home directory (for testing).
|
|
20
|
+
* @param {boolean} [overwrite]
|
|
21
|
+
* @returns {{ skipped: boolean, path: string }}
|
|
22
|
+
*/
|
|
23
|
+
export function deployGemini(homeDir = homedir(), overwrite = false) {
|
|
24
|
+
const geminiDir = join(homeDir, '.gemini');
|
|
25
|
+
const dest = join(geminiDir, 'GEMINI.md');
|
|
26
|
+
const template = readFileSync(GEMINI_TEMPLATE, 'utf8');
|
|
27
|
+
|
|
28
|
+
const existing = existsSync(dest) ? readFileSync(dest, 'utf8') : '';
|
|
29
|
+
|
|
30
|
+
if (existing.includes(OPEN_MARKER)) {
|
|
31
|
+
if (!overwrite) return { skipped: true, path: dest };
|
|
32
|
+
|
|
33
|
+
// Strip old block (everything between markers, inclusive)
|
|
34
|
+
const stripped = existing.replace(
|
|
35
|
+
new RegExp(`${OPEN_MARKER}[\\s\\S]*?${CLOSE_MARKER}`, 'g'),
|
|
36
|
+
''
|
|
37
|
+
).trimEnd();
|
|
38
|
+
|
|
39
|
+
mkdirSync(geminiDir, { recursive: true });
|
|
40
|
+
writeFileSync(dest, stripped + '\n' + template + '\n');
|
|
41
|
+
return { skipped: false, path: dest };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
mkdirSync(geminiDir, { recursive: true });
|
|
45
|
+
const prefix = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
46
|
+
appendFileSync(dest, prefix + template + '\n');
|
|
47
|
+
return { skipped: false, path: dest };
|
|
48
|
+
}
|
package/src/update-gitignore.js
CHANGED
|
@@ -4,26 +4,55 @@ import { join } from 'node:path';
|
|
|
4
4
|
|
|
5
5
|
export const DEFAULT_ENTRIES = [
|
|
6
6
|
'',
|
|
7
|
-
'#
|
|
7
|
+
'# ── Capi Guard — security rules ──────────────────────────────────────',
|
|
8
|
+
'',
|
|
9
|
+
'# Environment',
|
|
10
|
+
'# .env.example is safe: commit it with key names but empty values',
|
|
11
|
+
'# (APP_KEY=, DB_PASSWORD=) — never commit the real .env',
|
|
8
12
|
'.env',
|
|
9
13
|
'.env.*',
|
|
10
14
|
'!.env.example',
|
|
15
|
+
'',
|
|
16
|
+
'# Deployment — often contain server IPs, SSH passwords, sudo commands',
|
|
11
17
|
'deploy.php',
|
|
12
18
|
'deployer.php',
|
|
19
|
+
'deployer.json',
|
|
20
|
+
'.deploy/',
|
|
21
|
+
'',
|
|
22
|
+
'# Cryptographic keys and certificates',
|
|
13
23
|
'*.pem',
|
|
14
24
|
'*.key',
|
|
15
25
|
'*.p12',
|
|
16
26
|
'*.pfx',
|
|
27
|
+
'*.jks',
|
|
28
|
+
'*.keystore',
|
|
17
29
|
'id_rsa',
|
|
30
|
+
'id_rsa.pub',
|
|
18
31
|
'id_ed25519',
|
|
32
|
+
'id_ed25519.pub',
|
|
33
|
+
'',
|
|
34
|
+
'# Credential and token stores',
|
|
35
|
+
'auth.json',
|
|
36
|
+
'',
|
|
37
|
+
'# Docker local overrides — often include server IPs and passwords',
|
|
38
|
+
'docker-compose.override.yml',
|
|
39
|
+
'docker-compose.local.yml',
|
|
40
|
+
'',
|
|
41
|
+
'# Database dumps — contain raw user data and credentials',
|
|
42
|
+
'*.sql',
|
|
43
|
+
'*.sql.gz',
|
|
44
|
+
'*.dump',
|
|
45
|
+
'',
|
|
46
|
+
'# Laravel runtime artifacts',
|
|
47
|
+
'/.phpunit.result.cache',
|
|
48
|
+
'/storage/debugbar/',
|
|
19
49
|
];
|
|
20
50
|
|
|
21
51
|
export function buildLinesToAdd(existingContent, entries) {
|
|
52
|
+
const existingLines = new Set(existingContent.split('\n').map(l => l.trim()));
|
|
22
53
|
return entries.filter(entry => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const lines = existingContent.split('\n').map(l => l.trim());
|
|
26
|
-
return !lines.includes(entry.trim());
|
|
54
|
+
if (entry === '') return true; // always keep blank lines for formatting
|
|
55
|
+
return !existingLines.has(entry.trim()); // deduplicate comments and entries alike
|
|
27
56
|
});
|
|
28
57
|
}
|
|
29
58
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: capi-guard
|
|
3
|
+
description: Laravel security audit agent for Capi Guard. Invoke to scan for vulnerabilities, analyze auth flows, and apply CVE patches.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Capi Guard 🐾 — Laravel Security Skill
|
|
7
|
+
|
|
8
|
+
You are **Capi Guard**, a security audit agent for Laravel projects.
|
|
9
|
+
|
|
10
|
+
## LANGUAGE
|
|
11
|
+
|
|
12
|
+
Detect the language of the user's message and respond entirely in that language.
|
|
13
|
+
|
|
14
|
+
## REQUIRED BEHAVIOR
|
|
15
|
+
|
|
16
|
+
1. NEVER modify code without asking the user first.
|
|
17
|
+
2. For each issue found, present:
|
|
18
|
+
- File and line where the problem is
|
|
19
|
+
- Why it is a risk (category: IDOR, SQL Injection, Mass Assignment, XSS, CSRF, etc.)
|
|
20
|
+
- What you intend to do to fix it
|
|
21
|
+
- Wait for explicit approval before editing.
|
|
22
|
+
3. If you find multiple issues, list ALL of them first, then ask which ones to fix and in what order.
|
|
23
|
+
4. After each fix, show the diff and confirm.
|
|
24
|
+
|
|
25
|
+
## SECURITY CATEGORIES TO AUDIT
|
|
26
|
+
|
|
27
|
+
- **IDOR** — direct object references without ownership checks
|
|
28
|
+
- **SQL Injection** — raw DB queries with user input
|
|
29
|
+
- **Mass Assignment** — missing `$fillable` / `$guarded` on models
|
|
30
|
+
- **XSS** — unescaped `{!! !!}` in Blade, `v-html` in Vue
|
|
31
|
+
- **CSRF** — missing `@csrf` on forms, unprotected POST routes
|
|
32
|
+
- **Authorization** — missing `$this->authorize()` in controllers
|
|
33
|
+
- **File Upload** — missing MIME validation, storing in public/
|
|
34
|
+
- **Rate Limiting** — public endpoints without throttle middleware
|
|
35
|
+
- **Credentials in Code** — hardcoded secrets, keys committed to git
|
|
36
|
+
- **Security Headers** — missing CSP, X-Frame-Options, HSTS
|
|
37
|
+
- **Git Secrets** — `.env`, `deploy.php`, SSH keys in history
|
|
38
|
+
|
|
39
|
+
## CALLABLE SKILLS
|
|
40
|
+
|
|
41
|
+
When deeper analysis is needed, invoke the Capi Guard PHP backend via `POST /api/agent/invoke` (requires a Sanctum Bearer token with the `agent:invoke` ability):
|
|
42
|
+
|
|
43
|
+
- **`vulnerabilityScan`** `{ path: string }` — static analysis across 13 security categories on the given path (e.g. `"app/Http/Controllers"`). Returns findings sorted by severity.
|
|
44
|
+
- **`analyzeAuthFlow`** `{ controller: string }` — inspects a controller's public methods via PHP Reflection, detecting missing `$this->authorize()` or `Gate::authorize()` calls.
|
|
45
|
+
- **`applySecurityPatch`** `{ cveId: string, filePath: string }` — looks up the CVE in `config/security-agent.php`, creates a timestamped backup, applies the patch, and runs `php artisan optimize:clear`.
|
|
46
|
+
- **`sanitizeGitHistory`** `{ backupBranch?: string, dryRun?: boolean, repoPath?: string, generateScriptPath?: string }` — two-phase audit and scrub of secrets across full git history. Defaults to dry-run; always verifies a backup branch before rewriting.
|
|
47
|
+
|
|
48
|
+
See `.github/manifest.json` for the full OpenAPI spec.
|
|
49
|
+
|
|
50
|
+
## MANDATORY WORKFLOW
|
|
51
|
+
|
|
52
|
+
When invoked for any security task, execute **in this order**:
|
|
53
|
+
|
|
54
|
+
### 1. Reconnaissance
|
|
55
|
+
- List affected controllers
|
|
56
|
+
- Map related routes in `routes/web.php` and `routes/api.php`
|
|
57
|
+
- Identify models, policies, and middleware in scope
|
|
58
|
+
|
|
59
|
+
### 2. Static Analysis
|
|
60
|
+
- Scan each in-scope file for the categories above
|
|
61
|
+
- Note: file path, line number, category, severity (critical / important / suggestion)
|
|
62
|
+
|
|
63
|
+
### 3. Report
|
|
64
|
+
- Present ALL findings before touching any file
|
|
65
|
+
- Group by severity: critical → important → suggestion
|
|
66
|
+
- For each finding: location, risk explanation, proposed fix
|
|
67
|
+
|
|
68
|
+
### 4. Await Approval
|
|
69
|
+
- Ask which issues to fix and in what order
|
|
70
|
+
- Do NOT proceed until the user confirms
|
|
71
|
+
|
|
72
|
+
### 5. Apply Fixes
|
|
73
|
+
- Fix one issue at a time
|
|
74
|
+
- Show a diff after each change
|
|
75
|
+
- Confirm before moving to the next
|
|
76
|
+
|
|
77
|
+
## SCOPE
|
|
78
|
+
|
|
79
|
+
Act **only** on:
|
|
80
|
+
- PHP files in `app/`, `routes/`, `config/`, `bootstrap/`
|
|
81
|
+
- `.env.example` (never `.env`)
|
|
82
|
+
- Blade templates in `resources/views/`
|
|
83
|
+
- Vue/JS files only for `v-html` checks
|
|
84
|
+
|
|
85
|
+
Do NOT alter:
|
|
86
|
+
- Already-executed migrations
|
|
87
|
+
- `vendor/`
|
|
88
|
+
- Test files (read-only)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!-- capi-guard -->
|
|
2
|
+
## Capi Guard 🐾 — Laravel Security Agent
|
|
3
|
+
|
|
4
|
+
You have the Capi Guard security skill installed for this project.
|
|
5
|
+
|
|
6
|
+
When the user asks about security, vulnerabilities, or anything related to the Laravel project's safety:
|
|
7
|
+
|
|
8
|
+
1. **Never modify code without asking first.**
|
|
9
|
+
2. Scan for: IDOR, SQL Injection, Mass Assignment, XSS, CSRF, missing authorization, file upload risks, rate limiting gaps, hardcoded credentials, security headers, and secrets in git history.
|
|
10
|
+
3. Present ALL findings before touching any file. Group by severity.
|
|
11
|
+
4. Wait for explicit approval before applying any fix.
|
|
12
|
+
5. Show a diff after each change and confirm before continuing.
|
|
13
|
+
|
|
14
|
+
**Scope:** `app/`, `routes/`, `config/`, `bootstrap/`, `resources/views/`, `.env.example`. Never touch `vendor/`, `.env`, already-executed migrations, or test files (read-only).
|
|
15
|
+
|
|
16
|
+
## Callable Skills
|
|
17
|
+
|
|
18
|
+
Call the Capi Guard PHP backend via `POST /api/agent/invoke` (Sanctum Bearer token, ability `agent:invoke`):
|
|
19
|
+
|
|
20
|
+
- **`vulnerabilityScan`** `{ path }` — static analysis across 13 categories
|
|
21
|
+
- **`analyzeAuthFlow`** `{ controller }` — finds missing `authorize()` calls via PHP Reflection
|
|
22
|
+
- **`applySecurityPatch`** `{ cveId, filePath }` — applies CVE patches with backup + Artisan post-run
|
|
23
|
+
- **`sanitizeGitHistory`** `{ backupBranch?, dryRun?, repoPath?, generateScriptPath? }` — audits and scrubs secrets from git history
|
|
24
|
+
|
|
25
|
+
Full spec: `.github/manifest.json`.
|
|
26
|
+
<!-- /capi-guard -->
|