harness-everything 0.2.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 (169) hide show
  1. package/AGENTS.md +2 -0
  2. package/BENCHMARK_SOP.md +62 -0
  3. package/CHANGELOG.md +107 -0
  4. package/Dockerfile +18 -0
  5. package/LICENSE +201 -0
  6. package/README.md +197 -0
  7. package/VERIFICATION.md +347 -0
  8. package/bin/cli.js +97 -0
  9. package/build-multi-agent-system/AGENTS.md +50 -0
  10. package/build-multi-agent-system/README.md +63 -0
  11. package/build-multi-agent-system/SKILL.md +68 -0
  12. package/create-agent-launcher/SKILL.md +48 -0
  13. package/create-agent-launcher/examples/csharp-monorepo.md +123 -0
  14. package/create-agent-launcher/examples/typescript-cli.md +126 -0
  15. package/create-agent-launcher/guidelines/platform-claude.md +77 -0
  16. package/create-agent-launcher/guidelines/platform-codex.md +146 -0
  17. package/create-agent-launcher/guidelines/platform-copilot.md +74 -0
  18. package/create-agent-launcher/guidelines/platform-gemini.md +101 -0
  19. package/create-agent-launcher/guidelines/role-capabilities.md +108 -0
  20. package/create-agent-launcher/templates/README.md +17 -0
  21. package/create-agent-launcher/templates/agent-launcher.md +68 -0
  22. package/create-agent-launcher/templates/backend-developer-csharp.md +56 -0
  23. package/create-agent-launcher/templates/backend-developer-polyglot.md +62 -0
  24. package/create-agent-launcher/templates/backend-developer-ts.md +57 -0
  25. package/create-agent-launcher/templates/memory-keeper.md +37 -0
  26. package/create-agent-launcher/templates/requirement-analyzer.md +66 -0
  27. package/create-agent-launcher/templates/specialist-agent.md +34 -0
  28. package/create-agent-launcher/templates/task-verifier.md +56 -0
  29. package/create-agent-launcher/templates/tool-maker.md +33 -0
  30. package/create-agent-launcher/workflows/01-init.md +22 -0
  31. package/create-agent-launcher/workflows/02-analysis.md +26 -0
  32. package/create-agent-launcher/workflows/03-generation.md +29 -0
  33. package/create-agent-launcher/workflows/04-launcher.md +20 -0
  34. package/docker-verify.sh +266 -0
  35. package/docs/architecture.md +95 -0
  36. package/docs/philosophy.md +45 -0
  37. package/docs/reflection.md +34 -0
  38. package/docs/routing.md +45 -0
  39. package/docs/workflows/build-multi-agent-system.md +62 -0
  40. package/docs/workflows/create-agent-launcher.md +62 -0
  41. package/docs/workflows/environment-detection.md +63 -0
  42. package/docs/workflows/eval-harness.md +57 -0
  43. package/docs/workflows/fable-discipline.md +59 -0
  44. package/docs/workflows/fable-mode.md +32 -0
  45. package/docs/workflows/git-commit.md +62 -0
  46. package/docs/workflows/grill-me.md +62 -0
  47. package/docs/workflows/grill-with-docs.md +62 -0
  48. package/docs/workflows/harness-everything.md +92 -0
  49. package/docs/workflows/improve-codebase-architecture.md +60 -0
  50. package/docs/workflows/install-cognitive-os.md +60 -0
  51. package/docs/workflows/repo-docs.md +59 -0
  52. package/docs/workflows/rewrite-commits.md +60 -0
  53. package/docs/workflows/security-review.md +58 -0
  54. package/docs/workflows/self-evolve.md +59 -0
  55. package/docs/workflows/skill-creator.md +75 -0
  56. package/docs/workflows/skill-style.md +64 -0
  57. package/docs/workflows/tdd.md +39 -0
  58. package/docs/workflows/todo-driven-workflow.md +36 -0
  59. package/docs/workflows/using-git-worktrees.md +63 -0
  60. package/docs/workflows/verification-loop.md +69 -0
  61. package/docs/workflows/verify-before-claim.md +60 -0
  62. package/docs/workflows/zoom-out.md +62 -0
  63. package/environment-detection/SKILL.md +77 -0
  64. package/environment-detection/scripts/preflight.js +93 -0
  65. package/eval-framework/behavioral-test.js +105 -0
  66. package/eval-framework/cases/case1.json +5 -0
  67. package/eval-framework/cases/case2.json +5 -0
  68. package/eval-framework/cases/case3.json +5 -0
  69. package/eval-framework/cases/case4-zh-tier2.json +5 -0
  70. package/eval-framework/cases/case5-zh-tier3.json +5 -0
  71. package/eval-framework/mechanism-test.js +251 -0
  72. package/eval-framework/runner.js +56 -0
  73. package/eval-harness/SKILL.md +59 -0
  74. package/eval-harness/scripts/evaluate.js +61 -0
  75. package/fable-discipline/SKILL.md +38 -0
  76. package/fable-mode/CONTRACT-FORMAT.md +51 -0
  77. package/fable-mode/SKILL.md +51 -0
  78. package/fable-mode/agents/fable-orchestrator.md +92 -0
  79. package/fable-mode/agents/fable-verifier.md +32 -0
  80. package/fable-mode/agents/fable-worker-haiku.md +48 -0
  81. package/fable-mode/agents/fable-worker-sonnet.md +49 -0
  82. package/fable-mode/execution-guardrails/SKILL.md +87 -0
  83. package/fable-mode/fable-haiku/SKILL.md +48 -0
  84. package/git-commit/SKILL.md +55 -0
  85. package/git-commit/guides/ANGULAR_STYLE.md +61 -0
  86. package/git-commit/guides/COMMIT_GENERATION.md +24 -0
  87. package/git-commit/guides/LANGUAGE_DETECTION.md +16 -0
  88. package/git-commit/guides/MAIN_REPO.md +15 -0
  89. package/git-commit/guides/SUBMODULES.md +27 -0
  90. package/grill-me/SKILL.md +36 -0
  91. package/grill-with-docs/ADR-FORMAT.md +47 -0
  92. package/grill-with-docs/CONTEXT-FORMAT.md +63 -0
  93. package/grill-with-docs/SKILL.md +99 -0
  94. package/harness-everything/SKILL.md +141 -0
  95. package/harness-everything/examples/README.md +9 -0
  96. package/harness-everything/examples/todo-workflow.md +22 -0
  97. package/harness-everything/scripts/bootstrap.js +140 -0
  98. package/harness-everything/scripts/self-heal.js +189 -0
  99. package/harness-everything/scripts/tier-router.js +219 -0
  100. package/harness-everything/scripts/todo-cli.js +157 -0
  101. package/harness-everything/scripts/verify-gate.js +31 -0
  102. package/hooks/hooks.json +155 -0
  103. package/hooks/scripts/atomic-commit-check.js +68 -0
  104. package/hooks/scripts/boundary-guard.js +55 -0
  105. package/hooks/scripts/context-compact.js +114 -0
  106. package/hooks/scripts/contract-test.js +79 -0
  107. package/hooks/scripts/depth-guard.js +65 -0
  108. package/hooks/scripts/lib/harness-state.js +202 -0
  109. package/hooks/scripts/lib/platforms/claude.js +42 -0
  110. package/hooks/scripts/lib/platforms/codex.js +46 -0
  111. package/hooks/scripts/lib/platforms/continue.js +41 -0
  112. package/hooks/scripts/lib/platforms/copilot.js +41 -0
  113. package/hooks/scripts/lib/platforms/cursor.js +41 -0
  114. package/hooks/scripts/lib/platforms/hermes.js +32 -0
  115. package/hooks/scripts/lib/platforms/index.js +17 -0
  116. package/hooks/scripts/lib/platforms/worktrees.js +20 -0
  117. package/hooks/scripts/reset-circuit-breaker.js +30 -0
  118. package/hooks/scripts/rule-of-3-tracker.js +91 -0
  119. package/hooks/scripts/rule-of-3.js +176 -0
  120. package/hooks/scripts/state-persist.js +99 -0
  121. package/hooks/scripts/stop-gate.js +80 -0
  122. package/hooks/scripts/subagent-scope-guard.js +88 -0
  123. package/improve-codebase-architecture/SKILL.md +44 -0
  124. package/improve-codebase-architecture/guides/DEEPENING.md +37 -0
  125. package/improve-codebase-architecture/guides/HTML-REPORT.md +123 -0
  126. package/improve-codebase-architecture/guides/INTERFACE-DESIGN.md +44 -0
  127. package/improve-codebase-architecture/guides/LANGUAGE.md +53 -0
  128. package/install-cognitive-os/SKILL.md +95 -0
  129. package/package.json +22 -0
  130. package/repo-docs/SKILL.md +45 -0
  131. package/repo-docs/templates/agents-template.md +264 -0
  132. package/repo-docs/templates/knowledge-agents-template.md +50 -0
  133. package/repo-docs/templates/knowledge-readme-template.md +83 -0
  134. package/repo-docs/templates/manual-recon.md +117 -0
  135. package/repo-docs/templates/multi-skills-readme-template.md +72 -0
  136. package/repo-docs/templates/product-agents-template.md +78 -0
  137. package/repo-docs/templates/product-readme-template.md +95 -0
  138. package/repo-docs/templates/readme-template.md +136 -0
  139. package/repo-docs/templates/single-skill-readme-template.md +70 -0
  140. package/repo-docs/templates/skills-agents-template.md +60 -0
  141. package/rewrite-commits/SKILL.md +32 -0
  142. package/scripts/installer.js +649 -0
  143. package/scripts/lib/advisory-text.js +163 -0
  144. package/scripts/lib/claude-hooks.js +71 -0
  145. package/scripts/lib/gitignore.js +58 -0
  146. package/scripts/lib/manifest.js +82 -0
  147. package/scripts/lib/prompts.js +241 -0
  148. package/scripts/lib/skills.js +172 -0
  149. package/scripts/lib/workspace.js +28 -0
  150. package/security-review/SKILL.md +511 -0
  151. package/self-evolve/SKILL.md +54 -0
  152. package/self-evolve/scripts/persist-memory.js +40 -0
  153. package/self-evolve/scripts/self-regression.js +113 -0
  154. package/skill-creator/SKILL.md +91 -0
  155. package/skill-creator/references/quality-principles.md +81 -0
  156. package/skill-creator/references/testing-workflow.md +32 -0
  157. package/skill-style/SKILL.md +48 -0
  158. package/skill-style/STYLE.md +37 -0
  159. package/tdd/SKILL.md +53 -0
  160. package/tdd/guides/deep-modules.md +33 -0
  161. package/tdd/guides/interface-design.md +31 -0
  162. package/tdd/guides/mocking.md +59 -0
  163. package/tdd/guides/refactoring.md +10 -0
  164. package/tdd/guides/tests.md +61 -0
  165. package/todo-driven-workflow/SKILL.md +54 -0
  166. package/using-git-worktrees/SKILL.md +161 -0
  167. package/verification-loop/SKILL.md +138 -0
  168. package/verify-before-claim/SKILL.md +86 -0
  169. package/zoom-out/SKILL.md +90 -0
@@ -0,0 +1,172 @@
1
+ // Skill discovery, copy, and manifest-aware install/uninstall bookkeeping.
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const manifest = require('./manifest');
5
+
6
+ function copyDir(src, dest) {
7
+ fs.mkdirSync(dest, { recursive: true });
8
+ const entries = fs.readdirSync(src, { withFileTypes: true });
9
+ for (const entry of entries) {
10
+ const srcPath = path.join(src, entry.name);
11
+ const destPath = path.join(dest, entry.name);
12
+ if (entry.isDirectory()) {
13
+ copyDir(srcPath, destPath);
14
+ } else {
15
+ fs.copyFileSync(srcPath, destPath);
16
+ }
17
+ }
18
+ }
19
+
20
+ function getAvailableSkills(harnessSourceDir) {
21
+ const dirs = fs.readdirSync(harnessSourceDir, { withFileTypes: true });
22
+ const skills = [];
23
+ for (const dir of dirs) {
24
+ if (dir.isDirectory() && !dir.name.startsWith('.') && !['bin', 'docs', 'hooks', 'scripts', 'node_modules'].includes(dir.name)) {
25
+ const skillPath = path.join(harnessSourceDir, dir.name, 'SKILL.md');
26
+ if (fs.existsSync(skillPath)) {
27
+ skills.push(dir.name);
28
+ }
29
+ }
30
+ }
31
+ return skills.sort();
32
+ }
33
+
34
+ function parseFrontmatter(skillMdPath) {
35
+ if (!fs.existsSync(skillMdPath)) return null;
36
+ try {
37
+ const content = fs.readFileSync(skillMdPath, 'utf8');
38
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
39
+ if (!match) return null;
40
+ const yaml = match[1];
41
+ const get = (key) => {
42
+ const m = yaml.match(new RegExp(`^${key}:\\s*(.*)$`, 'm'));
43
+ return m ? m[1].trim().replace(/^['"]|['"]$/g, '') : '';
44
+ };
45
+ return { name: get('name'), description: get('description'), author: get('author'), version: get('version') };
46
+ } catch (e) {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ function getSkillInfo(harnessSourceDir, skillDirName) {
52
+ const fm = parseFrontmatter(path.join(harnessSourceDir, skillDirName, 'SKILL.md'));
53
+ if (!fm) return null;
54
+ return { id: skillDirName, name: fm.name || skillDirName, description: fm.description || '' };
55
+ }
56
+
57
+ // The one signal that lets uninstall tell "a skill this package shipped"
58
+ // apart from anything else living in the same shared skills folder (Cursor/
59
+ // Copilot/Codex/Continue skills keep their platform-native location, so it's
60
+ // still a shared folder) - including a user's own manually-installed skills
61
+ // and self-evolve's locally-generated `skills/generated/*` skills, neither
62
+ // of which carry this author string.
63
+ function isHarnessSkillDir(dirPath) {
64
+ const fm = parseFrontmatter(path.join(dirPath, 'SKILL.md'));
65
+ return !!fm && fm.author === manifest.HARNESS_AUTHOR;
66
+ }
67
+
68
+ // targetDirs entries: { path, label, manifestPath } - manifestPath is that
69
+ // target's own <platform-home>/harness-everything/manifest.json.
70
+ function installSkillsToTargets({ chosenSkills, targetDirs, harnessSourceDir, packageVersion }) {
71
+ for (const target of targetDirs) {
72
+ fs.mkdirSync(target.path, { recursive: true });
73
+ for (const skillName of chosenSkills) {
74
+ const src = path.join(harnessSourceDir, skillName);
75
+ const dest = path.join(target.path, skillName);
76
+ if (fs.existsSync(dest)) {
77
+ fs.rmSync(dest, { recursive: true, force: true });
78
+ }
79
+ copyDir(src, dest);
80
+ manifest.recordSkillInstall(target.manifestPath, packageVersion, skillName, dest);
81
+ console.log(` ✅ Installed skill: ${target.label}${skillName}/`);
82
+ }
83
+ }
84
+ }
85
+
86
+ function manifestTrackedSkills(manifestPath, scopeLabel) {
87
+ const data = manifest.readManifest(manifestPath);
88
+ const results = [];
89
+ for (const entry of data.skills || []) {
90
+ if (fs.existsSync(entry.dirPath) && isHarnessSkillDir(entry.dirPath)) {
91
+ results.push({ id: entry.id, scope: scopeLabel, dirPath: entry.dirPath, parentPath: path.dirname(entry.dirPath), manifestPath });
92
+ }
93
+ }
94
+ return results;
95
+ }
96
+
97
+ // Returns every skill this package can currently account for, scoped local +
98
+ // global. Never lists a directory just because it happens to live under a
99
+ // known skills folder - only manifest-recorded installs (or, as a fallback
100
+ // for pre-manifest / pre-reorg installs, directories that still self-identify
101
+ // via the author marker) qualify. This is what keeps "uninstall skills" from
102
+ // ever touching a skill this package didn't put there.
103
+ function getInstalledSkills(workspaceRoot, userHome) {
104
+ const results = [];
105
+ const globalAgentsDir = path.join(userHome, '.agents');
106
+
107
+ const manifestHomes = [
108
+ { home: path.join(workspaceRoot, '.claude'), scope: 'local (Claude)' },
109
+ { home: path.join(workspaceRoot, '.cursor'), scope: 'local (Cursor)' },
110
+ { home: path.join(workspaceRoot, '.github'), scope: 'local (Copilot)' },
111
+ { home: path.join(workspaceRoot, '.codex'), scope: 'local (Codex)' },
112
+ { home: path.join(workspaceRoot, '.continue'), scope: 'local (Continue)' },
113
+ { home: globalAgentsDir, scope: 'global' },
114
+ ];
115
+ for (const { home, scope } of manifestHomes) {
116
+ results.push(...manifestTrackedSkills(manifest.getManifestPath(home), scope));
117
+ }
118
+
119
+ // Legacy fallback for installs that predate per-platform manifests (or the
120
+ // old self-invented .harness/ local root, or - for Codex specifically - an
121
+ // earlier version's mistaken `.agents/skills` target; Codex's real project
122
+ // skills directory is `.codex/skills/`): scan the raw directories
123
+ // directly, but only ever list an entry that still self-identifies via the
124
+ // author marker - never a blind directory listing.
125
+ const legacyDirs = [
126
+ { path: path.join(workspaceRoot, '.harness', 'skills'), scope: 'local (legacy .harness)', manifestPath: manifest.getManifestPath(path.join(workspaceRoot, '.claude')) },
127
+ { path: path.join(workspaceRoot, '.cursor', 'skills'), scope: 'local (Cursor)', manifestPath: manifest.getManifestPath(path.join(workspaceRoot, '.cursor')) },
128
+ { path: path.join(workspaceRoot, '.github', 'skills'), scope: 'local (Copilot)', manifestPath: manifest.getManifestPath(path.join(workspaceRoot, '.github')) },
129
+ { path: path.join(workspaceRoot, '.codex', 'skills'), scope: 'local (Codex)', manifestPath: manifest.getManifestPath(path.join(workspaceRoot, '.codex')) },
130
+ { path: path.join(workspaceRoot, '.agents', 'skills'), scope: 'local (Codex, legacy .agents path)', manifestPath: manifest.getManifestPath(path.join(workspaceRoot, '.codex')) },
131
+ { path: path.join(workspaceRoot, '.continue', 'skills'), scope: 'local (Continue)', manifestPath: manifest.getManifestPath(path.join(workspaceRoot, '.continue')) },
132
+ { path: path.join(globalAgentsDir, 'skills'), scope: 'global', manifestPath: manifest.getManifestPath(globalAgentsDir) },
133
+ ];
134
+ const known = new Set(results.map(r => r.dirPath));
135
+ for (const item of legacyDirs) {
136
+ if (!fs.existsSync(item.path)) continue;
137
+ try {
138
+ const entries = fs.readdirSync(item.path, { withFileTypes: true });
139
+ for (const entry of entries) {
140
+ if (!entry.isDirectory()) continue;
141
+ const dirPath = path.join(item.path, entry.name);
142
+ if (known.has(dirPath)) continue;
143
+ if (isHarnessSkillDir(dirPath)) {
144
+ results.push({ id: entry.name, scope: item.scope, dirPath, parentPath: item.path, manifestPath: item.manifestPath });
145
+ known.add(dirPath);
146
+ }
147
+ }
148
+ } catch (e) {
149
+ // ignore unreadable dirs
150
+ }
151
+ }
152
+ return results;
153
+ }
154
+
155
+ function removeSkill(entry) {
156
+ if (fs.existsSync(entry.dirPath)) {
157
+ fs.rmSync(entry.dirPath, { recursive: true, force: true });
158
+ }
159
+ if (entry.manifestPath) {
160
+ manifest.removeSkillFromManifest(entry.manifestPath, entry.dirPath);
161
+ }
162
+ }
163
+
164
+ module.exports = {
165
+ copyDir,
166
+ getAvailableSkills,
167
+ getSkillInfo,
168
+ isHarnessSkillDir,
169
+ installSkillsToTargets,
170
+ getInstalledSkills,
171
+ removeSkill,
172
+ };
@@ -0,0 +1,28 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ function getWorkspaceRoot() {
6
+ let dir = path.resolve(process.cwd());
7
+ while (dir !== path.parse(dir).root) {
8
+ if (fs.existsSync(path.join(dir, '.git'))) return dir;
9
+ dir = path.dirname(dir);
10
+ }
11
+ return process.cwd();
12
+ }
13
+
14
+ function getUserPromptsDir() {
15
+ if (process.env.VSCODE_USER_PROMPTS_FOLDER) {
16
+ return process.env.VSCODE_USER_PROMPTS_FOLDER;
17
+ }
18
+ const home = os.homedir();
19
+ if (process.platform === 'win32') {
20
+ return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Code', 'User', 'prompts');
21
+ } else if (process.platform === 'darwin') {
22
+ return path.join(home, 'Library', 'Application Support', 'Code', 'User', 'prompts');
23
+ } else {
24
+ return path.join(home, '.config', 'Code', 'User', 'prompts');
25
+ }
26
+ }
27
+
28
+ module.exports = { getWorkspaceRoot, getUserPromptsDir };
@@ -0,0 +1,511 @@
1
+ ---
2
+ name: security-review
3
+ description: Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.
4
+ author: Miya Daniel | Harness Core Team
5
+ version: 0.2.0
6
+ metadata:
7
+ origin: ECC
8
+ ---
9
+
10
+ # Security Review Skill
11
+
12
+ ## 📋 Skill Contract
13
+
14
+ | Component | Specification |
15
+ | :--- | :--- |
16
+ | **Trigger / Input** | Task involves Auth, API Endpoints, File Uploads, User Input, or Secrets. Input: Target code file. |
17
+ | **Expected Output** | Security scan commands executed in terminal. Code modified to fix vulnerabilities. |
18
+ | **State Mutations** | Codebase hardened against injection/XSS/IDOR. |
19
+ | **Enforcement Gate** | You MUST use grep or standard security linters in the terminal to scan for secrets (`sk-`, `password`) or raw SQL interpolations BEFORE marking the security check as complete. |
20
+
21
+ This skill ensures all code follows security best practices and identifies potential vulnerabilities.
22
+
23
+ ## When to Activate
24
+
25
+ - Implementing authentication or authorization
26
+ - Handling user input or file uploads
27
+ - Creating new API endpoints
28
+ - Working with secrets or credentials
29
+ - Implementing payment features
30
+ - Storing or transmitting sensitive data
31
+ - Integrating third-party APIs
32
+
33
+ ## Security Checklist
34
+
35
+ ### 1. Secrets Management
36
+
37
+ #### FAIL: NEVER Do This
38
+ ```typescript
39
+ const apiKey = "sk-proj-xxxxx" // Hardcoded secret
40
+ const dbPassword = "password123" // In source code
41
+ ```
42
+
43
+ #### PASS: ALWAYS Do This
44
+ ```typescript
45
+ const apiKey = process.env.OPENAI_API_KEY
46
+ const dbUrl = process.env.DATABASE_URL
47
+
48
+ // Verify secrets exist
49
+ if (!apiKey) {
50
+ throw new Error('OPENAI_API_KEY not configured')
51
+ }
52
+ ```
53
+
54
+ #### Verification Steps
55
+ - [ ] No hardcoded API keys, tokens, or passwords
56
+ - [ ] All secrets in environment variables
57
+ - [ ] `.env.local` in .gitignore
58
+ - [ ] No secrets in git history
59
+ - [ ] Production secrets in hosting platform (Vercel, Railway)
60
+
61
+ ### 2. Input Validation
62
+
63
+ #### Always Validate User Input
64
+ ```typescript
65
+ import { z } from 'zod'
66
+
67
+ // Define validation schema
68
+ const CreateUserSchema = z.object({
69
+ email: z.string().email(),
70
+ name: z.string().min(1).max(100),
71
+ age: z.number().int().min(0).max(150)
72
+ })
73
+
74
+ // Validate before processing
75
+ export async function createUser(input: unknown) {
76
+ try {
77
+ const validated = CreateUserSchema.parse(input)
78
+ return await db.users.create(validated)
79
+ } catch (error) {
80
+ if (error instanceof z.ZodError) {
81
+ return { success: false, errors: error.issues }
82
+ }
83
+ throw error
84
+ }
85
+ }
86
+ ```
87
+
88
+ #### File Upload Validation
89
+ ```typescript
90
+ function validateFileUpload(file: File) {
91
+ // Size check (5MB max)
92
+ const maxSize = 5 * 1024 * 1024
93
+ if (file.size > maxSize) {
94
+ throw new Error('File too large (max 5MB)')
95
+ }
96
+
97
+ // Type check
98
+ const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
99
+ if (!allowedTypes.includes(file.type)) {
100
+ throw new Error('Invalid file type')
101
+ }
102
+
103
+ // Extension check
104
+ const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']
105
+ const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0]
106
+ if (!extension || !allowedExtensions.includes(extension)) {
107
+ throw new Error('Invalid file extension')
108
+ }
109
+
110
+ return true
111
+ }
112
+ ```
113
+
114
+ #### Verification Steps
115
+ - [ ] All user inputs validated with schemas
116
+ - [ ] File uploads restricted (size, type, extension)
117
+ - [ ] No direct use of user input in queries
118
+ - [ ] Whitelist validation (not blacklist)
119
+ - [ ] Error messages don't leak sensitive info
120
+
121
+ ### 3. SQL Injection Prevention
122
+
123
+ #### FAIL: NEVER Concatenate SQL
124
+ ```typescript
125
+ // DANGEROUS - SQL Injection vulnerability
126
+ const query = `SELECT * FROM users WHERE email = '${userEmail}'`
127
+ await db.query(query)
128
+ ```
129
+
130
+ #### PASS: ALWAYS Use Parameterized Queries
131
+ ```typescript
132
+ // Safe - parameterized query
133
+ const { data } = await supabase
134
+ .from('users')
135
+ .select('*')
136
+ .eq('email', userEmail)
137
+
138
+ // Or with raw SQL
139
+ await db.query(
140
+ 'SELECT * FROM users WHERE email = $1',
141
+ [userEmail]
142
+ )
143
+ ```
144
+
145
+ #### Verification Steps
146
+ - [ ] All database queries use parameterized queries
147
+ - [ ] No string concatenation in SQL
148
+ - [ ] ORM/query builder used correctly
149
+ - [ ] Supabase queries properly sanitized
150
+
151
+ ### 4. Authentication & Authorization
152
+
153
+ #### JWT Token Handling
154
+ ```typescript
155
+ // FAIL: WRONG: localStorage (vulnerable to XSS)
156
+ localStorage.setItem('token', token)
157
+
158
+ // PASS: CORRECT: httpOnly cookies
159
+ res.setHeader('Set-Cookie',
160
+ `token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
161
+ ```
162
+
163
+ #### Authorization Checks
164
+ ```typescript
165
+ export async function deleteUser(userId: string, requesterId: string) {
166
+ // ALWAYS verify authorization first
167
+ const requester = await db.users.findUnique({
168
+ where: { id: requesterId }
169
+ })
170
+
171
+ if (requester.role !== 'admin') {
172
+ return NextResponse.json(
173
+ { error: 'Unauthorized' },
174
+ { status: 403 }
175
+ )
176
+ }
177
+
178
+ // Proceed with deletion
179
+ await db.users.delete({ where: { id: userId } })
180
+ }
181
+ ```
182
+
183
+ #### Row Level Security (Supabase)
184
+ ```sql
185
+ -- Enable RLS on all tables
186
+ ALTER TABLE users ENABLE ROW LEVEL SECURITY;
187
+
188
+ -- Users can only view their own data
189
+ CREATE POLICY "Users view own data"
190
+ ON users FOR SELECT
191
+ USING (auth.uid() = id);
192
+
193
+ -- Users can only update their own data
194
+ CREATE POLICY "Users update own data"
195
+ ON users FOR UPDATE
196
+ USING (auth.uid() = id);
197
+ ```
198
+
199
+ #### Verification Steps
200
+ - [ ] Tokens stored in httpOnly cookies (not localStorage)
201
+ - [ ] Authorization checks before sensitive operations
202
+ - [ ] Row Level Security enabled in Supabase
203
+ - [ ] Role-based access control implemented
204
+ - [ ] Session management secure
205
+
206
+ ### 5. XSS Prevention
207
+
208
+ #### Sanitize HTML
209
+ ```typescript
210
+ import DOMPurify from 'isomorphic-dompurify'
211
+
212
+ // ALWAYS sanitize user-provided HTML
213
+ function renderUserContent(html: string) {
214
+ const clean = DOMPurify.sanitize(html, {
215
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
216
+ ALLOWED_ATTR: []
217
+ })
218
+ return <div dangerouslySetInnerHTML={{ __html: clean }} />
219
+ }
220
+ ```
221
+
222
+ #### Content Security Policy
223
+
224
+ Start strict and loosen only with a documented removal plan. Do not default to
225
+ `'unsafe-inline'` or `'unsafe-eval'`; they neutralize much of CSP's protection
226
+ and should be treated as temporary compatibility debt.
227
+
228
+ ```typescript
229
+ // next.config.js
230
+ const securityHeaders = [
231
+ {
232
+ key: 'Content-Security-Policy',
233
+ value: `
234
+ default-src 'self';
235
+ base-uri 'self';
236
+ object-src 'none';
237
+ frame-ancestors 'none';
238
+ script-src 'self';
239
+ style-src 'self';
240
+ img-src 'self' data: https:;
241
+ font-src 'self';
242
+ connect-src 'self' https://api.example.com;
243
+ `.replace(/\s{2,}/g, ' ').trim()
244
+ }
245
+ ]
246
+ ```
247
+
248
+ #### Verification Steps
249
+ - [ ] User-provided HTML sanitized
250
+ - [ ] CSP headers configured
251
+ - [ ] No unvalidated dynamic content rendering
252
+ - [ ] React's built-in XSS protection used
253
+
254
+ ### 6. CSRF Protection
255
+
256
+ #### CSRF Tokens
257
+ ```typescript
258
+ import { csrf } from '@/lib/csrf'
259
+
260
+ export async function POST(request: Request) {
261
+ const token = request.headers.get('X-CSRF-Token')
262
+
263
+ if (!csrf.verify(token)) {
264
+ return NextResponse.json(
265
+ { error: 'Invalid CSRF token' },
266
+ { status: 403 }
267
+ )
268
+ }
269
+
270
+ // Process request
271
+ }
272
+ ```
273
+
274
+ #### SameSite Cookies
275
+ ```typescript
276
+ res.setHeader('Set-Cookie',
277
+ `session=${sessionId}; HttpOnly; Secure; SameSite=Strict`)
278
+ ```
279
+
280
+ #### Verification Steps
281
+ - [ ] CSRF tokens on state-changing operations
282
+ - [ ] SameSite=Strict on all cookies
283
+ - [ ] Double-submit cookie pattern implemented
284
+
285
+ ### 7. Rate Limiting
286
+
287
+ #### API Rate Limiting
288
+ ```typescript
289
+ import rateLimit from 'express-rate-limit'
290
+
291
+ const limiter = rateLimit({
292
+ windowMs: 15 * 60 * 1000, // 15 minutes
293
+ max: 100, // 100 requests per window
294
+ message: 'Too many requests'
295
+ })
296
+
297
+ // Apply to routes
298
+ app.use('/api/', limiter)
299
+ ```
300
+
301
+ #### Expensive Operations
302
+ ```typescript
303
+ // Aggressive rate limiting for searches
304
+ const searchLimiter = rateLimit({
305
+ windowMs: 60 * 1000, // 1 minute
306
+ max: 10, // 10 requests per minute
307
+ message: 'Too many search requests'
308
+ })
309
+
310
+ app.use('/api/search', searchLimiter)
311
+ ```
312
+
313
+ #### Verification Steps
314
+ - [ ] Rate limiting on all API endpoints
315
+ - [ ] Stricter limits on expensive operations
316
+ - [ ] IP-based rate limiting
317
+ - [ ] User-based rate limiting (authenticated)
318
+
319
+ ### 8. Sensitive Data Exposure
320
+
321
+ #### Logging
322
+ ```typescript
323
+ // FAIL: WRONG: Logging sensitive data
324
+ console.log('User login:', { email, password })
325
+ console.log('Payment:', { cardNumber, cvv })
326
+
327
+ // PASS: CORRECT: Redact sensitive data
328
+ console.log('User login:', { email, userId })
329
+ console.log('Payment:', { last4: card.last4, userId })
330
+ ```
331
+
332
+ #### Error Messages
333
+ ```typescript
334
+ // FAIL: WRONG: Exposing internal details
335
+ catch (error) {
336
+ return NextResponse.json(
337
+ { error: error.message, stack: error.stack },
338
+ { status: 500 }
339
+ )
340
+ }
341
+
342
+ // PASS: CORRECT: Generic error messages
343
+ catch (error) {
344
+ console.error('Internal error:', error)
345
+ return NextResponse.json(
346
+ { error: 'An error occurred. Please try again.' },
347
+ { status: 500 }
348
+ )
349
+ }
350
+ ```
351
+
352
+ #### Verification Steps
353
+ - [ ] No passwords, tokens, or secrets in logs
354
+ - [ ] Error messages generic for users
355
+ - [ ] Detailed errors only in server logs
356
+ - [ ] No stack traces exposed to users
357
+
358
+ ### 9. Blockchain Security (Solana)
359
+
360
+ #### Wallet Verification
361
+ ```typescript
362
+ import { verify } from '@solana/web3.js'
363
+
364
+ async function verifyWalletOwnership(
365
+ publicKey: string,
366
+ signature: string,
367
+ message: string
368
+ ) {
369
+ try {
370
+ const isValid = verify(
371
+ Buffer.from(message),
372
+ Buffer.from(signature, 'base64'),
373
+ Buffer.from(publicKey, 'base64')
374
+ )
375
+ return isValid
376
+ } catch (error) {
377
+ return false
378
+ }
379
+ }
380
+ ```
381
+
382
+ #### Transaction Verification
383
+ ```typescript
384
+ async function verifyTransaction(transaction: Transaction) {
385
+ // Verify recipient
386
+ if (transaction.to !== expectedRecipient) {
387
+ throw new Error('Invalid recipient')
388
+ }
389
+
390
+ // Verify amount
391
+ if (transaction.amount > maxAmount) {
392
+ throw new Error('Amount exceeds limit')
393
+ }
394
+
395
+ // Verify user has sufficient balance
396
+ const balance = await getBalance(transaction.from)
397
+ if (balance < transaction.amount) {
398
+ throw new Error('Insufficient balance')
399
+ }
400
+
401
+ return true
402
+ }
403
+ ```
404
+
405
+ #### Verification Steps
406
+ - [ ] Wallet signatures verified
407
+ - [ ] Transaction details validated
408
+ - [ ] Balance checks before transactions
409
+ - [ ] No blind transaction signing
410
+
411
+ ### 10. Dependency Security
412
+
413
+ #### Regular Updates
414
+ ```bash
415
+ # Check for vulnerabilities
416
+ npm audit
417
+
418
+ # Fix automatically fixable issues
419
+ npm audit fix
420
+
421
+ # Update dependencies
422
+ npm update
423
+
424
+ # Check for outdated packages
425
+ npm outdated
426
+ ```
427
+
428
+ #### Lock Files
429
+ ```bash
430
+ # ALWAYS commit lock files
431
+ git add package-lock.json
432
+
433
+ # Use in CI/CD for reproducible builds
434
+ npm ci # Instead of npm install
435
+ ```
436
+
437
+ #### Verification Steps
438
+ - [ ] Dependencies up to date
439
+ - [ ] No known vulnerabilities (npm audit clean)
440
+ - [ ] Lock files committed
441
+ - [ ] Dependabot enabled on GitHub
442
+ - [ ] Regular security updates
443
+
444
+ ## Security Testing
445
+
446
+ ### Automated Security Tests
447
+ ```typescript
448
+ // Test authentication
449
+ test('requires authentication', async () => {
450
+ const response = await fetch('/api/protected')
451
+ expect(response.status).toBe(401)
452
+ })
453
+
454
+ // Test authorization
455
+ test('requires admin role', async () => {
456
+ const response = await fetch('/api/admin', {
457
+ headers: { Authorization: `Bearer ${userToken}` }
458
+ })
459
+ expect(response.status).toBe(403)
460
+ })
461
+
462
+ // Test input validation
463
+ test('rejects invalid input', async () => {
464
+ const response = await fetch('/api/users', {
465
+ method: 'POST',
466
+ body: JSON.stringify({ email: 'not-an-email' })
467
+ })
468
+ expect(response.status).toBe(400)
469
+ })
470
+
471
+ // Test rate limiting
472
+ test('enforces rate limits', async () => {
473
+ const requests = Array(101).fill(null).map(() =>
474
+ fetch('/api/endpoint')
475
+ )
476
+
477
+ const responses = await Promise.all(requests)
478
+ const tooManyRequests = responses.filter(r => r.status === 429)
479
+
480
+ expect(tooManyRequests.length).toBeGreaterThan(0)
481
+ })
482
+ ```
483
+
484
+ ## Pre-Deployment Security Checklist
485
+
486
+ Before ANY production deployment:
487
+
488
+ - [ ] **Secrets**: No hardcoded secrets, all in env vars
489
+ - [ ] **Input Validation**: All user inputs validated
490
+ - [ ] **SQL Injection**: All queries parameterized
491
+ - [ ] **XSS**: User content sanitized
492
+ - [ ] **CSRF**: Protection enabled
493
+ - [ ] **Authentication**: Proper token handling
494
+ - [ ] **Authorization**: Role checks in place
495
+ - [ ] **Rate Limiting**: Enabled on all endpoints
496
+ - [ ] **HTTPS**: Enforced in production
497
+ - [ ] **Security Headers**: CSP, X-Frame-Options configured
498
+ - [ ] **Error Handling**: No sensitive data in errors
499
+ - [ ] **Logging**: No sensitive data logged
500
+ - [ ] **Dependencies**: Up to date, no vulnerabilities
501
+ - [ ] **Row Level Security**: Enabled in Supabase
502
+ - [ ] **CORS**: Properly configured
503
+ - [ ] **File Uploads**: Validated (size, type)
504
+ - [ ] **Wallet Signatures**: Verified (if blockchain)
505
+
506
+ ## Resources
507
+
508
+ - [OWASP Top 10](https://owasp.org/www-project-top-ten/)
509
+ - [Next.js Security](https://nextjs.org/docs/security)
510
+ - [Supabase Security](https://supabase.com/docs/guides/auth)
511
+ - [Web Security Academy](https://portswigger.net/web-security)