secure-coding-rules 2.0.0 → 2.0.1
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 +71 -60
- package/package.json +5 -5
- package/src/__tests__/adapters.test.js +201 -0
- package/src/__tests__/loader.test.js +68 -0
- package/src/adapters/agents.js +2 -2
- package/src/adapters/claude.js +42 -3
- package/src/adapters/copilot.js +46 -2
- package/src/i18n.js +271 -0
- package/src/index.js +222 -56
- package/src/loader.js +33 -20
- package/src/prompts.js +79 -77
- package/src/templates/frameworks/express-security.md +130 -0
- package/src/templates/frameworks/nextjs-security.md +149 -0
- package/src/templates/frameworks/react-security.md +120 -0
- package/src/templates/typescript/typescript-security.md +113 -0
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# secure-coding-rules
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/secure-coding-rules)
|
|
4
|
-
[](https://github.com/
|
|
4
|
+
[](https://github.com/kwakseongjae/js-secure-coding-prompt-templates/blob/main/LICENSE)
|
|
5
5
|
[](https://nodejs.org)
|
|
6
6
|
|
|
7
|
-
**OWASP Top 10 2025**
|
|
7
|
+
Apply **OWASP Top 10 2025** JavaScript/TypeScript security rules to your AI coding assistant with one command.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Auto-generates security guidelines for CLAUDE.md, .cursor/rules, .windsurf/rules, copilot-instructions.md, and AGENTS.md.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -16,107 +16,107 @@
|
|
|
16
16
|
npx secure-coding-rules
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
1. AI
|
|
21
|
-
2.
|
|
22
|
-
3.
|
|
19
|
+
Interactive prompts:
|
|
20
|
+
1. Select AI tool (Claude Code / Cursor / Windsurf / Copilot / AGENTS.md)
|
|
21
|
+
2. Select framework (React / Vue / Node.js / Vanilla) - **auto-detected**
|
|
22
|
+
3. Select security categories (all or individual)
|
|
23
23
|
|
|
24
24
|
### Auto Mode
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Analyzes your project and applies optimal settings automatically:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
npx secure-coding-rules --yes
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
-
|
|
33
|
-
- package.json
|
|
34
|
-
- CI/CD
|
|
32
|
+
- Auto-detects existing AI tool config files and updates them
|
|
33
|
+
- Auto-detects framework from package.json (React, Vue, Node.js, etc.)
|
|
34
|
+
- Works in CI/CD and other non-interactive environments
|
|
35
35
|
|
|
36
36
|
### Status Check
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Check current project security rule status:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
npx secure-coding-rules --check
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## Supported AI Tools
|
|
45
45
|
|
|
46
|
-
| AI Tool | Output |
|
|
47
|
-
|
|
48
|
-
| **Claude Code** | `CLAUDE.md` |
|
|
49
|
-
| **Cursor** | `.cursor/rules/*.mdc` |
|
|
50
|
-
| **Windsurf** | `.windsurf/rules/*.md` |
|
|
51
|
-
| **GitHub Copilot** | `.github/copilot-instructions.md` |
|
|
52
|
-
| **AGENTS.md** | `AGENTS.md` |
|
|
46
|
+
| AI Tool | Output | Existing files |
|
|
47
|
+
|---------|--------|----------------|
|
|
48
|
+
| **Claude Code** | `CLAUDE.md` | Auto-merge |
|
|
49
|
+
| **Cursor** | `.cursor/rules/*.mdc` | Per-category files |
|
|
50
|
+
| **Windsurf** | `.windsurf/rules/*.md` | Per-category files |
|
|
51
|
+
| **GitHub Copilot** | `.github/copilot-instructions.md` | Auto-merge |
|
|
52
|
+
| **AGENTS.md** | `AGENTS.md` | Auto-merge |
|
|
53
53
|
|
|
54
|
-
##
|
|
54
|
+
## Security Categories (OWASP Top 10 2025)
|
|
55
55
|
|
|
56
56
|
| Code | Category | Description |
|
|
57
57
|
|------|----------|-------------|
|
|
58
|
-
| A01 | Broken Access Control | RBAC/ABAC, IDOR
|
|
59
|
-
| A02 | Security Misconfiguration |
|
|
60
|
-
| A03 | Supply Chain Failures | npm audit, lockfile
|
|
61
|
-
| A04 | Cryptographic Failures |
|
|
58
|
+
| A01 | Broken Access Control | RBAC/ABAC, IDOR prevention, server-side authz |
|
|
59
|
+
| A02 | Security Misconfiguration | Security headers, CORS, env vars |
|
|
60
|
+
| A03 | Supply Chain Failures | npm audit, lockfile integrity, SRI **(New in 2025)** |
|
|
61
|
+
| A04 | Cryptographic Failures | Secure hashing, encryption, key management |
|
|
62
62
|
| A05 | Injection | XSS, SQLi, NoSQLi, Command Injection |
|
|
63
|
-
| A06 | Insecure Design | Threat modeling,
|
|
64
|
-
| A07 | Authentication Failures | MFA,
|
|
65
|
-
| A08 | Data Integrity Failures | SRI,
|
|
66
|
-
| A09 | Logging & Alerting |
|
|
67
|
-
| A10 | Error Handling | Fail-safe
|
|
63
|
+
| A06 | Insecure Design | Threat modeling, least privilege |
|
|
64
|
+
| A07 | Authentication Failures | MFA, session management, password policy |
|
|
65
|
+
| A08 | Data Integrity Failures | SRI, safe deserialization, CI/CD security |
|
|
66
|
+
| A09 | Logging & Alerting | Security logging, sensitive data masking |
|
|
67
|
+
| A10 | Error Handling | Fail-safe defaults, error info leakage **(New in 2025)** |
|
|
68
68
|
|
|
69
69
|
### Frontend Modules
|
|
70
70
|
|
|
71
71
|
| Code | Category | Description |
|
|
72
72
|
|------|----------|-------------|
|
|
73
|
-
| FE-01 | XSS Prevention | DOM
|
|
74
|
-
| FE-02 | CSRF Protection |
|
|
75
|
-
| FE-03 | Content Security Policy | CSP
|
|
76
|
-
| FE-04 | Secure State |
|
|
73
|
+
| FE-01 | XSS Prevention | Safe DOM manipulation, sanitization |
|
|
74
|
+
| FE-02 | CSRF Protection | Token-based defense, SameSite cookies |
|
|
75
|
+
| FE-03 | Content Security Policy | CSP headers, nonce, reporting |
|
|
76
|
+
| FE-04 | Secure State | Safe state management, in-memory tokens |
|
|
77
77
|
|
|
78
|
-
##
|
|
78
|
+
## How It Works
|
|
79
79
|
|
|
80
|
-
###
|
|
80
|
+
### Rule Format
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
All security rules follow a consistent, AI-friendly structure:
|
|
83
83
|
|
|
84
84
|
```markdown
|
|
85
85
|
### 1. Rule Title
|
|
86
|
-
- **DO**:
|
|
87
|
-
- **DON'T**:
|
|
88
|
-
- **WHY**:
|
|
86
|
+
- **DO**: What to do (specific instruction)
|
|
87
|
+
- **DON'T**: What to avoid
|
|
88
|
+
- **WHY**: Why it matters
|
|
89
89
|
|
|
90
90
|
## Code Examples
|
|
91
91
|
### Bad Practice / Good Practice
|
|
92
92
|
|
|
93
93
|
## Quick Checklist
|
|
94
|
-
- [ ]
|
|
94
|
+
- [ ] Checklist items
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
###
|
|
97
|
+
### File Merging
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
If CLAUDE.md or other config files already exist, existing content is preserved and only the security section is added/updated:
|
|
100
100
|
|
|
101
101
|
```html
|
|
102
102
|
<!-- js-secure-coding:start -->
|
|
103
|
-
(
|
|
103
|
+
(only this region is updated)
|
|
104
104
|
<!-- js-secure-coding:end -->
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
Re-running replaces only the marked region with the latest version.
|
|
108
108
|
|
|
109
|
-
###
|
|
109
|
+
### Auto-Detection
|
|
110
110
|
|
|
111
|
-
`secure-coding-rules
|
|
111
|
+
`secure-coding-rules` analyzes your project at runtime:
|
|
112
112
|
|
|
113
|
-
- **AI
|
|
114
|
-
-
|
|
115
|
-
-
|
|
113
|
+
- **AI tools**: Checks for CLAUDE.md, .cursor/, .windsurf/, .github/
|
|
114
|
+
- **Framework**: Reads package.json dependencies (React, Vue, Express, etc.)
|
|
115
|
+
- **Smart prompts**: Detected items are highlighted and prioritized in interactive mode
|
|
116
116
|
|
|
117
|
-
##
|
|
117
|
+
## Manual Usage
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
You can also copy markdown files from `src/templates/` directly without the CLI:
|
|
120
120
|
|
|
121
121
|
```
|
|
122
122
|
src/templates/
|
|
@@ -141,18 +141,29 @@ src/templates/
|
|
|
141
141
|
## CLI Options
|
|
142
142
|
|
|
143
143
|
```
|
|
144
|
-
npx secure-coding-rules
|
|
145
|
-
npx secure-coding-rules --yes
|
|
146
|
-
npx secure-coding-rules --check
|
|
147
|
-
npx secure-coding-rules --
|
|
148
|
-
npx secure-coding-rules --
|
|
144
|
+
npx secure-coding-rules Interactive mode (auto-detect)
|
|
145
|
+
npx secure-coding-rules --yes Smart defaults
|
|
146
|
+
npx secure-coding-rules --check Project security status
|
|
147
|
+
npx secure-coding-rules --dry-run Preview (no file writes)
|
|
148
|
+
npx secure-coding-rules --lang ko Run in Korean (한국어)
|
|
149
|
+
npx secure-coding-rules --help Help
|
|
150
|
+
npx secure-coding-rules --version Version
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Language / 다국어
|
|
154
|
+
|
|
155
|
+
Auto-detects system locale (`LANG` env). Override with `--lang`:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npx secure-coding-rules --lang en # English (default)
|
|
159
|
+
npx secure-coding-rules --lang ko # 한국어
|
|
149
160
|
```
|
|
150
161
|
|
|
151
162
|
## Legacy Templates
|
|
152
163
|
|
|
153
|
-
v1.0
|
|
164
|
+
Original v1.0 prompt templates are preserved in the `legacy/` directory.
|
|
154
165
|
|
|
155
|
-
##
|
|
166
|
+
## References
|
|
156
167
|
|
|
157
168
|
- [OWASP Top 10 2025](https://owasp.org/Top10/2025/)
|
|
158
169
|
- [Node.js Security Best Practices](https://nodejs.org/en/learn/getting-started/security-best-practices)
|
|
@@ -161,7 +172,7 @@ v1.0 원본 템플릿은 `legacy/` 디렉토리에 보존되어 있습니다.
|
|
|
161
172
|
|
|
162
173
|
## Contributing
|
|
163
174
|
|
|
164
|
-
|
|
175
|
+
PRs welcome! New security rules, AI tool adapters, or improvements to existing content.
|
|
165
176
|
|
|
166
177
|
## License
|
|
167
178
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "secure-coding-rules",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "OWASP 2025 security rules for AI coding assistants. Auto-apply to CLAUDE.md, Cursor, Windsurf, Copilot, AGENTS.md with one command.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -44,16 +44,16 @@
|
|
|
44
44
|
],
|
|
45
45
|
"author": {
|
|
46
46
|
"name": "Gwak Seong-jae",
|
|
47
|
-
"url": "https://github.com/
|
|
47
|
+
"url": "https://github.com/kwakseongjae"
|
|
48
48
|
},
|
|
49
49
|
"license": "MIT",
|
|
50
50
|
"repository": {
|
|
51
51
|
"type": "git",
|
|
52
|
-
"url": "git+https://github.com/
|
|
52
|
+
"url": "git+https://github.com/kwakseongjae/js-secure-coding-prompt-templates.git"
|
|
53
53
|
},
|
|
54
|
-
"homepage": "https://github.com/
|
|
54
|
+
"homepage": "https://github.com/kwakseongjae/js-secure-coding-prompt-templates#readme",
|
|
55
55
|
"bugs": {
|
|
56
|
-
"url": "https://github.com/
|
|
56
|
+
"url": "https://github.com/kwakseongjae/js-secure-coding-prompt-templates/issues"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">=18.0.0"
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import * as claude from '../adapters/claude.js';
|
|
4
|
+
import * as copilot from '../adapters/copilot.js';
|
|
5
|
+
import * as agents from '../adapters/agents.js';
|
|
6
|
+
import * as cursor from '../adapters/cursor.js';
|
|
7
|
+
import * as windsurf from '../adapters/windsurf.js';
|
|
8
|
+
|
|
9
|
+
const MOCK_TEMPLATE = `# Test Security Rules
|
|
10
|
+
|
|
11
|
+
> OWASP Top 10 2025 - A01: Test
|
|
12
|
+
|
|
13
|
+
## Rules
|
|
14
|
+
|
|
15
|
+
### 1. Test Rule One
|
|
16
|
+
- **DO**: Do the right thing.
|
|
17
|
+
- **DON'T**: Do the wrong thing.
|
|
18
|
+
- **WHY**: Because security matters.
|
|
19
|
+
|
|
20
|
+
### 2. Test Rule Two
|
|
21
|
+
- **DO**: Validate input.
|
|
22
|
+
- **DON'T**: Trust user data.
|
|
23
|
+
- **WHY**: Injection attacks are common.
|
|
24
|
+
|
|
25
|
+
## Code Examples
|
|
26
|
+
|
|
27
|
+
### Bad Practice
|
|
28
|
+
\`\`\`javascript
|
|
29
|
+
eval(userInput);
|
|
30
|
+
\`\`\`
|
|
31
|
+
|
|
32
|
+
### Good Practice
|
|
33
|
+
\`\`\`javascript
|
|
34
|
+
sanitize(userInput);
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
## Quick Checklist
|
|
38
|
+
- [ ] Input validated
|
|
39
|
+
- [ ] Output encoded
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
function mockTemplates() {
|
|
43
|
+
return new Map([['access-control', MOCK_TEMPLATE]]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe('claude adapter', () => {
|
|
47
|
+
it('formats templates with markers', () => {
|
|
48
|
+
const output = claude.format(mockTemplates());
|
|
49
|
+
assert.ok(output.includes('<!-- js-secure-coding:start -->'));
|
|
50
|
+
assert.ok(output.includes('<!-- js-secure-coding:end -->'));
|
|
51
|
+
assert.ok(output.includes('A01: Broken Access Control'));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('extracts Rules and Checklist, not Code Examples', () => {
|
|
55
|
+
const output = claude.format(mockTemplates());
|
|
56
|
+
assert.ok(output.includes('Test Rule One'));
|
|
57
|
+
assert.ok(output.includes('Input validated'));
|
|
58
|
+
assert.ok(!output.includes('eval(userInput)'));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('merges into existing content', () => {
|
|
62
|
+
const existing = '# My Project\n\nSome content here.';
|
|
63
|
+
const section = claude.format(mockTemplates());
|
|
64
|
+
const merged = claude.merge(existing, section);
|
|
65
|
+
assert.ok(merged.startsWith('# My Project'));
|
|
66
|
+
assert.ok(merged.includes('<!-- js-secure-coding:start -->'));
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('replaces existing section on re-merge', () => {
|
|
70
|
+
const first = claude.format(mockTemplates());
|
|
71
|
+
const existing = `# Project\n\n${first}\n\n# Other stuff`;
|
|
72
|
+
const newSection = claude.format(mockTemplates(), { framework: 'react' });
|
|
73
|
+
const merged = claude.merge(existing, newSection);
|
|
74
|
+
|
|
75
|
+
const starts = merged.match(/<!-- js-secure-coding:start -->/g);
|
|
76
|
+
assert.equal(starts.length, 1, 'Should have exactly one start marker');
|
|
77
|
+
assert.ok(merged.includes('Framework: react'));
|
|
78
|
+
assert.ok(merged.includes('# Other stuff'));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('handles empty file append without leading whitespace', () => {
|
|
82
|
+
const merged = claude.merge('', claude.format(mockTemplates()));
|
|
83
|
+
assert.ok(!merged.startsWith('\n'));
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('copilot adapter', () => {
|
|
88
|
+
it('formats with correct markers', () => {
|
|
89
|
+
const output = copilot.format(mockTemplates());
|
|
90
|
+
assert.ok(output.includes('<!-- js-secure-coding:start -->'));
|
|
91
|
+
assert.ok(output.includes('Security Coding Guidelines'));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('extracts DO, DONT, and WHY rules', () => {
|
|
95
|
+
const output = copilot.format(mockTemplates());
|
|
96
|
+
assert.ok(output.includes('**DO**'));
|
|
97
|
+
assert.ok(output.includes("**DON'T**"));
|
|
98
|
+
assert.ok(output.includes('**WHY**'));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('handles empty file merge', () => {
|
|
102
|
+
const merged = copilot.merge('', copilot.format(mockTemplates()));
|
|
103
|
+
assert.ok(!merged.startsWith('\n'));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('agents adapter', () => {
|
|
108
|
+
it('formats with markers and title', () => {
|
|
109
|
+
const output = agents.format(mockTemplates());
|
|
110
|
+
assert.ok(output.includes('# Security Guidelines'));
|
|
111
|
+
assert.ok(output.includes('<!-- js-secure-coding:start -->'));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('strips original title from template', () => {
|
|
115
|
+
const output = agents.format(mockTemplates());
|
|
116
|
+
assert.ok(!output.includes('# Test Security Rules'));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('handles empty file merge', () => {
|
|
120
|
+
const merged = agents.merge('', agents.format(mockTemplates()));
|
|
121
|
+
assert.ok(!merged.startsWith('\n'));
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('claude adapter - directory mode', () => {
|
|
126
|
+
it('generates individual rule files via formatMultiple', () => {
|
|
127
|
+
const files = claude.formatMultiple(mockTemplates());
|
|
128
|
+
assert.equal(files.size, 1);
|
|
129
|
+
assert.ok(files.has('security-access-control.md'));
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('has rulesDir defined', () => {
|
|
133
|
+
assert.equal(claude.rulesDir, '.claude/rules');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('generates reference text via formatReference', () => {
|
|
137
|
+
const ref = claude.formatReference(['access-control', 'injection']);
|
|
138
|
+
assert.ok(ref.includes('<!-- js-secure-coding:start -->'));
|
|
139
|
+
assert.ok(ref.includes('.claude/rules/'));
|
|
140
|
+
assert.ok(ref.includes('security-access-control.md'));
|
|
141
|
+
assert.ok(ref.includes('security-injection.md'));
|
|
142
|
+
assert.ok(ref.includes('A01'));
|
|
143
|
+
assert.ok(ref.includes('A05'));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('reference can be merged into existing CLAUDE.md', () => {
|
|
147
|
+
const existing = '# My Project\n\nSome content.';
|
|
148
|
+
const ref = claude.formatReference(['access-control']);
|
|
149
|
+
const merged = claude.merge(existing, ref);
|
|
150
|
+
assert.ok(merged.startsWith('# My Project'));
|
|
151
|
+
assert.ok(merged.includes('.claude/rules/'));
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('copilot adapter - directory mode', () => {
|
|
156
|
+
it('generates individual rule files via formatMultiple', () => {
|
|
157
|
+
const files = copilot.formatMultiple(mockTemplates());
|
|
158
|
+
assert.equal(files.size, 1);
|
|
159
|
+
assert.ok(files.has('security-access-control.md'));
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('has rulesDir defined', () => {
|
|
163
|
+
assert.equal(copilot.rulesDir, '.github/instructions');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('generates reference text via formatReference', () => {
|
|
167
|
+
const ref = copilot.formatReference(['access-control']);
|
|
168
|
+
assert.ok(ref.includes('.github/instructions/'));
|
|
169
|
+
assert.ok(ref.includes('security-access-control.md'));
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('cursor adapter', () => {
|
|
174
|
+
it('generates multiple .mdc files', () => {
|
|
175
|
+
const files = cursor.formatMultiple(mockTemplates());
|
|
176
|
+
assert.equal(files.size, 1);
|
|
177
|
+
assert.ok(files.has('security-access-control.mdc'));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('includes frontmatter with description', () => {
|
|
181
|
+
const files = cursor.formatMultiple(mockTemplates());
|
|
182
|
+
const content = files.get('security-access-control.mdc');
|
|
183
|
+
assert.ok(content.startsWith('---'));
|
|
184
|
+
assert.ok(content.includes('description:'));
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('windsurf adapter', () => {
|
|
189
|
+
it('generates multiple .md files', () => {
|
|
190
|
+
const files = windsurf.formatMultiple(mockTemplates());
|
|
191
|
+
assert.equal(files.size, 1);
|
|
192
|
+
assert.ok(files.has('security-access-control.md'));
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('strips original title and adds own', () => {
|
|
196
|
+
const files = windsurf.formatMultiple(mockTemplates());
|
|
197
|
+
const content = files.get('security-access-control.md');
|
|
198
|
+
assert.ok(content.startsWith('# A01: Broken Access Control'));
|
|
199
|
+
assert.ok(!content.includes('# Test Security Rules'));
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { loadTemplate, loadTemplates, getCategoryInfo } from '../loader.js';
|
|
4
|
+
|
|
5
|
+
describe('loader', () => {
|
|
6
|
+
describe('loadTemplate', () => {
|
|
7
|
+
it('loads a core template', async () => {
|
|
8
|
+
const content = await loadTemplate('access-control');
|
|
9
|
+
assert.ok(content);
|
|
10
|
+
assert.ok(content.includes('## Rules'));
|
|
11
|
+
assert.ok(content.includes('## Quick Checklist'));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('loads a frontend template', async () => {
|
|
15
|
+
const content = await loadTemplate('xss-prevention');
|
|
16
|
+
assert.ok(content);
|
|
17
|
+
assert.ok(content.includes('## Rules'));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('returns null for unknown category', async () => {
|
|
21
|
+
const content = await loadTemplate('nonexistent-category');
|
|
22
|
+
assert.equal(content, null);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('loadTemplates', () => {
|
|
27
|
+
it('loads multiple templates', async () => {
|
|
28
|
+
const templates = await loadTemplates(['access-control', 'injection']);
|
|
29
|
+
assert.equal(templates.size, 2);
|
|
30
|
+
assert.ok(templates.has('access-control'));
|
|
31
|
+
assert.ok(templates.has('injection'));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('loads all 14 templates', async () => {
|
|
35
|
+
const all = [
|
|
36
|
+
'access-control', 'security-config', 'supply-chain', 'cryptographic',
|
|
37
|
+
'injection', 'secure-design', 'authentication', 'data-integrity',
|
|
38
|
+
'logging-alerting', 'error-handling', 'xss-prevention',
|
|
39
|
+
'csrf-protection', 'csp', 'secure-state',
|
|
40
|
+
];
|
|
41
|
+
const templates = await loadTemplates(all);
|
|
42
|
+
assert.equal(templates.size, 14);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('skips unknown categories gracefully', async () => {
|
|
46
|
+
const templates = await loadTemplates(['access-control', 'fake']);
|
|
47
|
+
assert.equal(templates.size, 1);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('getCategoryInfo', () => {
|
|
52
|
+
it('returns correct OWASP code for core categories', () => {
|
|
53
|
+
assert.equal(getCategoryInfo('access-control').owasp, 'A01');
|
|
54
|
+
assert.equal(getCategoryInfo('injection').owasp, 'A05');
|
|
55
|
+
assert.equal(getCategoryInfo('error-handling').owasp, 'A10');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('returns correct info for frontend categories', () => {
|
|
59
|
+
assert.equal(getCategoryInfo('xss-prevention').group, 'frontend');
|
|
60
|
+
assert.equal(getCategoryInfo('csp').owasp, 'FE-03');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('returns fallback for unknown category', () => {
|
|
64
|
+
const info = getCategoryInfo('unknown');
|
|
65
|
+
assert.equal(info.owasp, '??');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
package/src/adapters/agents.js
CHANGED
|
@@ -13,11 +13,11 @@ const SECTION_START = '<!-- js-secure-coding:start -->';
|
|
|
13
13
|
const SECTION_END = '<!-- js-secure-coding:end -->';
|
|
14
14
|
|
|
15
15
|
export function format(templates, options = {}) {
|
|
16
|
-
const { framework = 'vanilla' } = options;
|
|
16
|
+
const { framework = 'vanilla', version = '2.0.0' } = options;
|
|
17
17
|
const lines = [];
|
|
18
18
|
|
|
19
19
|
lines.push(SECTION_START);
|
|
20
|
-
lines.push(
|
|
20
|
+
lines.push(`<!-- version: ${version} -->`);
|
|
21
21
|
lines.push('');
|
|
22
22
|
lines.push('# Security Guidelines');
|
|
23
23
|
lines.push('');
|
package/src/adapters/claude.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getCategoryInfo } from '../loader.js';
|
|
|
6
6
|
|
|
7
7
|
export const name = 'Claude Code';
|
|
8
8
|
export const outputPath = 'CLAUDE.md';
|
|
9
|
+
export const rulesDir = '.claude/rules';
|
|
9
10
|
export const description = 'Generates CLAUDE.md for Claude Code';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -15,15 +16,15 @@ const SECTION_START = '<!-- js-secure-coding:start -->';
|
|
|
15
16
|
const SECTION_END = '<!-- js-secure-coding:end -->';
|
|
16
17
|
|
|
17
18
|
export function format(templates, options = {}) {
|
|
18
|
-
const { framework = 'vanilla' } = options;
|
|
19
|
+
const { framework = 'vanilla', version = '2.0.0' } = options;
|
|
19
20
|
const lines = [];
|
|
20
21
|
|
|
21
22
|
lines.push(SECTION_START);
|
|
22
|
-
lines.push(
|
|
23
|
+
lines.push(`<!-- version: ${version} -->`);
|
|
23
24
|
lines.push('');
|
|
24
25
|
lines.push('# Security Rules (OWASP 2025)');
|
|
25
26
|
lines.push('');
|
|
26
|
-
lines.push(`> Auto-generated by
|
|
27
|
+
lines.push(`> Auto-generated by secure-coding-rules v${version} | Framework: ${framework}`);
|
|
27
28
|
lines.push('> Reference: https://owasp.org/Top10/2025/');
|
|
28
29
|
lines.push('');
|
|
29
30
|
|
|
@@ -61,6 +62,44 @@ export function merge(existingContent, newSection) {
|
|
|
61
62
|
return (trimmed ? trimmed + '\n\n' : '') + newSection + '\n';
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Format templates into individual files for directory mode
|
|
67
|
+
* Returns Map<filename, content>
|
|
68
|
+
*/
|
|
69
|
+
export function formatMultiple(templates, options = {}) {
|
|
70
|
+
const files = new Map();
|
|
71
|
+
for (const [category, content] of templates) {
|
|
72
|
+
files.set(`security-${category}.md`, content);
|
|
73
|
+
}
|
|
74
|
+
return files;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generate reference text for CLAUDE.md pointing to rules directory
|
|
79
|
+
*/
|
|
80
|
+
export function formatReference(categories, options = {}) {
|
|
81
|
+
const { version = '2.0.0' } = options;
|
|
82
|
+
const lines = [
|
|
83
|
+
SECTION_START,
|
|
84
|
+
`<!-- version: ${version} -->`,
|
|
85
|
+
'',
|
|
86
|
+
'# Security Rules (OWASP 2025)',
|
|
87
|
+
'',
|
|
88
|
+
`> Auto-generated by secure-coding-rules v${version}`,
|
|
89
|
+
'> Full rules are in .claude/rules/security-*.md',
|
|
90
|
+
'',
|
|
91
|
+
'Security rules are loaded from `.claude/rules/` directory:',
|
|
92
|
+
'',
|
|
93
|
+
];
|
|
94
|
+
for (const cat of categories) {
|
|
95
|
+
const info = getCategoryInfo(cat);
|
|
96
|
+
lines.push(`- \`security-${cat}.md\` - ${info.owasp}: ${info.title}`);
|
|
97
|
+
}
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push(SECTION_END);
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
64
103
|
function extractEssentials(content) {
|
|
65
104
|
const lines = content.split('\n');
|
|
66
105
|
const result = [];
|