@oculum/scanner 1.0.11 → 1.0.12
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/dist/ai-context/index.d.ts +6 -0
- package/dist/ai-context/index.d.ts.map +1 -0
- package/dist/ai-context/index.js +13 -0
- package/dist/ai-context/index.js.map +1 -0
- package/dist/ai-context/manager.d.ts +67 -0
- package/dist/ai-context/manager.d.ts.map +1 -0
- package/dist/ai-context/manager.js +104 -0
- package/dist/ai-context/manager.js.map +1 -0
- package/dist/category-filter.d.ts +125 -0
- package/dist/category-filter.d.ts.map +1 -0
- package/dist/category-filter.js +360 -0
- package/dist/category-filter.js.map +1 -0
- package/dist/filtering/context-adjustments.d.ts +23 -0
- package/dist/filtering/context-adjustments.d.ts.map +1 -0
- package/dist/filtering/context-adjustments.js +100 -0
- package/dist/filtering/context-adjustments.js.map +1 -0
- package/dist/filtering/index.d.ts +3 -0
- package/dist/filtering/index.d.ts.map +1 -0
- package/dist/filtering/index.js +8 -0
- package/dist/filtering/index.js.map +1 -0
- package/dist/filtering/pipeline.d.ts +48 -0
- package/dist/filtering/pipeline.d.ts.map +1 -0
- package/dist/filtering/pipeline.js +76 -0
- package/dist/filtering/pipeline.js.map +1 -0
- package/dist/formatters/ai-context.d.ts +23 -0
- package/dist/formatters/ai-context.d.ts.map +1 -0
- package/dist/formatters/ai-context.js +238 -0
- package/dist/formatters/ai-context.js.map +1 -0
- package/dist/formatters/github-comment.d.ts +1 -1
- package/dist/formatters/github-comment.d.ts.map +1 -1
- package/dist/formatters/github-comment.js +2 -2
- package/dist/formatters/github-comment.js.map +1 -1
- package/dist/formatters/ide/claude-code.d.ts +17 -0
- package/dist/formatters/ide/claude-code.d.ts.map +1 -0
- package/dist/formatters/ide/claude-code.js +94 -0
- package/dist/formatters/ide/claude-code.js.map +1 -0
- package/dist/formatters/ide/cursor.d.ts +13 -0
- package/dist/formatters/ide/cursor.d.ts.map +1 -0
- package/dist/formatters/ide/cursor.js +125 -0
- package/dist/formatters/ide/cursor.js.map +1 -0
- package/dist/formatters/ide/index.d.ts +62 -0
- package/dist/formatters/ide/index.d.ts.map +1 -0
- package/dist/formatters/ide/index.js +184 -0
- package/dist/formatters/ide/index.js.map +1 -0
- package/dist/formatters/ide/windsurf.d.ts +13 -0
- package/dist/formatters/ide/windsurf.d.ts.map +1 -0
- package/dist/formatters/ide/windsurf.js +117 -0
- package/dist/formatters/ide/windsurf.js.map +1 -0
- package/dist/formatters/index.d.ts +2 -0
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +17 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +272 -44
- package/dist/index.js.map +1 -1
- package/dist/layer1/comments.d.ts +4 -1
- package/dist/layer1/comments.d.ts.map +1 -1
- package/dist/layer1/comments.js +1 -1
- package/dist/layer1/comments.js.map +1 -1
- package/dist/layer1/config-audit.d.ts +4 -1
- package/dist/layer1/config-audit.d.ts.map +1 -1
- package/dist/layer1/config-audit.js +45 -11
- package/dist/layer1/config-audit.js.map +1 -1
- package/dist/layer1/config-mcp-audit.d.ts +4 -1
- package/dist/layer1/config-mcp-audit.d.ts.map +1 -1
- package/dist/layer1/config-mcp-audit.js +2 -2
- package/dist/layer1/config-mcp-audit.js.map +1 -1
- package/dist/layer1/entropy.d.ts +4 -1
- package/dist/layer1/entropy.d.ts.map +1 -1
- package/dist/layer1/entropy.js +212 -1
- package/dist/layer1/entropy.js.map +1 -1
- package/dist/layer1/file-flags.d.ts +4 -1
- package/dist/layer1/file-flags.d.ts.map +1 -1
- package/dist/layer1/file-flags.js +12 -5
- package/dist/layer1/file-flags.js.map +1 -1
- package/dist/layer1/index.d.ts.map +1 -1
- package/dist/layer1/index.js +14 -19
- package/dist/layer1/index.js.map +1 -1
- package/dist/layer1/patterns.d.ts +4 -1
- package/dist/layer1/patterns.d.ts.map +1 -1
- package/dist/layer1/patterns.js +34 -4
- package/dist/layer1/patterns.js.map +1 -1
- package/dist/layer1/urls.d.ts +4 -1
- package/dist/layer1/urls.d.ts.map +1 -1
- package/dist/layer1/urls.js +162 -14
- package/dist/layer1/urls.js.map +1 -1
- package/dist/layer1/weak-crypto.d.ts +4 -1
- package/dist/layer1/weak-crypto.d.ts.map +1 -1
- package/dist/layer1/weak-crypto.js +144 -7
- package/dist/layer1/weak-crypto.js.map +1 -1
- package/dist/layer2/ai-agent-tools.d.ts +4 -1
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
- package/dist/layer2/ai-agent-tools.js +661 -2
- package/dist/layer2/ai-agent-tools.js.map +1 -1
- package/dist/layer2/ai-endpoint-protection.d.ts +2 -0
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
- package/dist/layer2/ai-endpoint-protection.js +1 -1
- package/dist/layer2/ai-endpoint-protection.js.map +1 -1
- package/dist/layer2/ai-execution-sinks.d.ts +4 -1
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
- package/dist/layer2/ai-execution-sinks.js +252 -43
- package/dist/layer2/ai-execution-sinks.js.map +1 -1
- package/dist/layer2/ai-fingerprinting.d.ts +4 -1
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
- package/dist/layer2/ai-fingerprinting.js +25 -32
- package/dist/layer2/ai-fingerprinting.js.map +1 -1
- package/dist/layer2/ai-mcp-security.d.ts +4 -1
- package/dist/layer2/ai-mcp-security.d.ts.map +1 -1
- package/dist/layer2/ai-mcp-security.js +200 -2
- package/dist/layer2/ai-mcp-security.js.map +1 -1
- package/dist/layer2/ai-package-hallucination.d.ts +4 -1
- package/dist/layer2/ai-package-hallucination.d.ts.map +1 -1
- package/dist/layer2/ai-package-hallucination.js +136 -4
- package/dist/layer2/ai-package-hallucination.js.map +1 -1
- package/dist/layer2/ai-prompt-hygiene.d.ts +4 -1
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
- package/dist/layer2/ai-prompt-hygiene.js +342 -28
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
- package/dist/layer2/ai-rag-safety.d.ts +4 -1
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
- package/dist/layer2/ai-rag-safety.js +82 -2
- package/dist/layer2/ai-rag-safety.js.map +1 -1
- package/dist/layer2/ai-schema-validation.d.ts +4 -1
- package/dist/layer2/ai-schema-validation.d.ts.map +1 -1
- package/dist/layer2/ai-schema-validation.js +2 -2
- package/dist/layer2/ai-schema-validation.js.map +1 -1
- package/dist/layer2/auth-antipatterns.d.ts +2 -0
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
- package/dist/layer2/auth-antipatterns.js +205 -20
- package/dist/layer2/auth-antipatterns.js.map +1 -1
- package/dist/layer2/byok-patterns.d.ts +4 -1
- package/dist/layer2/byok-patterns.d.ts.map +1 -1
- package/dist/layer2/byok-patterns.js +2 -2
- package/dist/layer2/byok-patterns.js.map +1 -1
- package/dist/layer2/dangerous-functions/dom-xss.d.ts +9 -4
- package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -1
- package/dist/layer2/dangerous-functions/dom-xss.js +73 -22
- package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -1
- package/dist/layer2/dangerous-functions/index.d.ts +4 -1
- package/dist/layer2/dangerous-functions/index.d.ts.map +1 -1
- package/dist/layer2/dangerous-functions/index.js +551 -20
- package/dist/layer2/dangerous-functions/index.js.map +1 -1
- package/dist/layer2/dangerous-functions/math-random.d.ts +54 -4
- package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -1
- package/dist/layer2/dangerous-functions/math-random.js +241 -16
- package/dist/layer2/dangerous-functions/math-random.js.map +1 -1
- package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -1
- package/dist/layer2/dangerous-functions/patterns.js +3 -1
- package/dist/layer2/dangerous-functions/patterns.js.map +1 -1
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +3 -2
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -1
- package/dist/layer2/dangerous-functions/utils/control-flow.js +41 -120
- package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -1
- package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -1
- package/dist/layer2/dangerous-functions/utils/helpers.js +26 -3
- package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -1
- package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -1
- package/dist/layer2/dangerous-functions/utils/schema-validation.js +14 -1
- package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -1
- package/dist/layer2/data-exposure.d.ts +4 -1
- package/dist/layer2/data-exposure.d.ts.map +1 -1
- package/dist/layer2/data-exposure.js +11 -38
- package/dist/layer2/data-exposure.js.map +1 -1
- package/dist/layer2/framework-checks.d.ts +4 -1
- package/dist/layer2/framework-checks.d.ts.map +1 -1
- package/dist/layer2/framework-checks.js +2 -2
- package/dist/layer2/framework-checks.js.map +1 -1
- package/dist/layer2/index.d.ts +9 -1
- package/dist/layer2/index.d.ts.map +1 -1
- package/dist/layer2/index.js +57 -51
- package/dist/layer2/index.js.map +1 -1
- package/dist/layer2/logic-gates.d.ts +4 -1
- package/dist/layer2/logic-gates.d.ts.map +1 -1
- package/dist/layer2/logic-gates.js +54 -20
- package/dist/layer2/logic-gates.js.map +1 -1
- package/dist/layer2/model-supply-chain.d.ts +4 -1
- package/dist/layer2/model-supply-chain.d.ts.map +1 -1
- package/dist/layer2/model-supply-chain.js +72 -4
- package/dist/layer2/model-supply-chain.js.map +1 -1
- package/dist/layer2/risky-imports.d.ts +4 -1
- package/dist/layer2/risky-imports.d.ts.map +1 -1
- package/dist/layer2/risky-imports.js +2 -2
- package/dist/layer2/risky-imports.js.map +1 -1
- package/dist/layer2/variables.d.ts +4 -1
- package/dist/layer2/variables.d.ts.map +1 -1
- package/dist/layer2/variables.js +2 -2
- package/dist/layer2/variables.js.map +1 -1
- package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -1
- package/dist/layer3/anthropic/auto-dismiss.js +11 -0
- package/dist/layer3/anthropic/auto-dismiss.js.map +1 -1
- package/dist/modes/incremental.js +1 -1
- package/dist/tiers.d.ts +2 -2
- package/dist/tiers.d.ts.map +1 -1
- package/dist/tiers.js +7 -7
- package/dist/tiers.js.map +1 -1
- package/dist/types.d.ts +78 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/code-analysis.d.ts +39 -0
- package/dist/utils/code-analysis.d.ts.map +1 -0
- package/dist/utils/code-analysis.js +159 -0
- package/dist/utils/code-analysis.js.map +1 -0
- package/dist/utils/comment-analyzer.d.ts +38 -0
- package/dist/utils/comment-analyzer.d.ts.map +1 -0
- package/dist/utils/comment-analyzer.js +218 -0
- package/dist/utils/comment-analyzer.js.map +1 -0
- package/dist/utils/context-helpers.d.ts +108 -1
- package/dist/utils/context-helpers.d.ts.map +1 -1
- package/dist/utils/context-helpers.js +351 -2
- package/dist/utils/context-helpers.js.map +1 -1
- package/dist/utils/environment-context.d.ts +76 -0
- package/dist/utils/environment-context.d.ts.map +1 -0
- package/dist/utils/environment-context.js +271 -0
- package/dist/utils/environment-context.js.map +1 -0
- package/dist/utils/intent-detector.d.ts +66 -0
- package/dist/utils/intent-detector.d.ts.map +1 -0
- package/dist/utils/intent-detector.js +282 -0
- package/dist/utils/intent-detector.js.map +1 -0
- package/dist/utils/parsed-file.d.ts +51 -0
- package/dist/utils/parsed-file.d.ts.map +1 -0
- package/dist/utils/parsed-file.js +95 -0
- package/dist/utils/parsed-file.js.map +1 -0
- package/dist/utils/route-hierarchy.d.ts +50 -0
- package/dist/utils/route-hierarchy.d.ts.map +1 -0
- package/dist/utils/route-hierarchy.js +226 -0
- package/dist/utils/route-hierarchy.js.map +1 -0
- package/dist/utils/schema-semantics.d.ts +45 -0
- package/dist/utils/schema-semantics.d.ts.map +1 -0
- package/dist/utils/schema-semantics.js +193 -0
- package/dist/utils/schema-semantics.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/benchmark/fixtures/layer2/index.ts +12 -0
- package/src/__tests__/benchmark/fixtures/layer2/phase5-excessive-agency.ts +580 -0
- package/src/__tests__/benchmark/fixtures/layer2/sprint6-ai-enhancements.ts +515 -0
- package/src/__tests__/benchmark/run-depth-validation.ts +9 -9
- package/src/__tests__/category-filter.test.ts +478 -0
- package/src/__tests__/regression/known-false-positives.test.ts +490 -0
- package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +18 -14
- package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +0 -9
- package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +1 -1
- package/src/__tests__/validation/run-validation.ts +7 -7
- package/src/ai-context/__tests__/manager.test.ts +193 -0
- package/src/ai-context/index.ts +15 -0
- package/src/ai-context/manager.ts +145 -0
- package/src/baseline/__tests__/manager.test.ts +2 -2
- package/src/category-filter.ts +400 -0
- package/src/filtering/__tests__/pipeline.test.ts +134 -0
- package/src/filtering/context-adjustments.ts +111 -0
- package/src/filtering/index.ts +10 -0
- package/src/filtering/pipeline.ts +130 -0
- package/src/formatters/__tests__/ai-context.test.ts +254 -0
- package/src/formatters/ai-context.ts +302 -0
- package/src/formatters/github-comment.ts +3 -3
- package/src/formatters/ide/__tests__/ide.test.ts +319 -0
- package/src/formatters/ide/claude-code.ts +110 -0
- package/src/formatters/ide/cursor.ts +147 -0
- package/src/formatters/ide/index.ts +216 -0
- package/src/formatters/ide/windsurf.ts +135 -0
- package/src/formatters/index.ts +24 -0
- package/src/index.ts +312 -34
- package/src/layer1/comments.ts +3 -1
- package/src/layer1/config-audit.ts +50 -11
- package/src/layer1/config-mcp-audit.ts +4 -2
- package/src/layer1/entropy.ts +234 -1
- package/src/layer1/file-flags.ts +17 -6
- package/src/layer1/index.ts +14 -18
- package/src/layer1/patterns.ts +42 -4
- package/src/layer1/urls.ts +188 -14
- package/src/layer1/weak-crypto.ts +168 -16
- package/src/layer2/ai-agent-tools.ts +707 -2
- package/src/layer2/ai-endpoint-protection.ts +3 -1
- package/src/layer2/ai-execution-sinks.ts +265 -43
- package/src/layer2/ai-fingerprinting.ts +28 -32
- package/src/layer2/ai-mcp-security.ts +206 -3
- package/src/layer2/ai-package-hallucination.ts +153 -4
- package/src/layer2/ai-prompt-hygiene.ts +369 -26
- package/src/layer2/ai-rag-safety.ts +85 -2
- package/src/layer2/ai-schema-validation.ts +4 -2
- package/src/layer2/auth-antipatterns.ts +230 -20
- package/src/layer2/byok-patterns.ts +4 -2
- package/src/layer2/dangerous-functions/dom-xss.ts +94 -22
- package/src/layer2/dangerous-functions/index.ts +635 -51
- package/src/layer2/dangerous-functions/math-random.ts +268 -16
- package/src/layer2/dangerous-functions/patterns.ts +3 -1
- package/src/layer2/dangerous-functions/utils/control-flow.ts +8 -135
- package/src/layer2/dangerous-functions/utils/schema-validation.ts +16 -1
- package/src/layer2/data-exposure.ts +13 -38
- package/src/layer2/framework-checks.ts +4 -2
- package/src/layer2/index.ts +69 -50
- package/src/layer2/logic-gates.ts +59 -22
- package/src/layer2/model-supply-chain.ts +79 -4
- package/src/layer2/risky-imports.ts +4 -2
- package/src/layer2/variables.ts +4 -2
- package/src/layer3/anthropic/auto-dismiss.ts +11 -0
- package/src/modes/incremental.ts +1 -1
- package/src/tiers.ts +9 -9
- package/src/types.ts +122 -8
- package/src/utils/__tests__/code-analysis.test.ts +165 -0
- package/src/utils/__tests__/parsed-file.test.ts +124 -0
- package/src/utils/code-analysis.ts +179 -0
- package/src/utils/comment-analyzer.ts +249 -0
- package/src/utils/context-helpers.ts +408 -2
- package/src/utils/environment-context.ts +304 -0
- package/src/utils/intent-detector.ts +318 -0
- package/src/utils/parsed-file.ts +103 -0
- package/src/utils/route-hierarchy.ts +250 -0
- package/src/utils/schema-semantics.ts +233 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE Integration Module
|
|
3
|
+
* Exports formatters and utilities for IDE-specific config files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs'
|
|
7
|
+
import { join, dirname } from 'path'
|
|
8
|
+
|
|
9
|
+
// Export formatters
|
|
10
|
+
export { formatCursorRules } from './cursor'
|
|
11
|
+
export { formatWindsurfRules } from './windsurf'
|
|
12
|
+
export { formatClaudeCodeSection, OCULUM_SECTION_START, OCULUM_SECTION_END } from './claude-code'
|
|
13
|
+
|
|
14
|
+
/** Supported IDE types */
|
|
15
|
+
export type IDEType = 'cursor' | 'windsurf' | 'claude-code'
|
|
16
|
+
|
|
17
|
+
/** Default file paths for each IDE */
|
|
18
|
+
export const IDE_FILE_PATHS: Record<IDEType, string> = {
|
|
19
|
+
cursor: '.cursor/rules/security.mdc',
|
|
20
|
+
windsurf: '.windsurfrules',
|
|
21
|
+
'claude-code': 'CLAUDE.md',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Result of a file write operation */
|
|
25
|
+
export interface WriteIDEFileResult {
|
|
26
|
+
success: boolean
|
|
27
|
+
path: string
|
|
28
|
+
error?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Result of a file clear operation */
|
|
32
|
+
export interface ClearIDEFilesResult {
|
|
33
|
+
success: boolean
|
|
34
|
+
clearedFiles: string[]
|
|
35
|
+
errors: string[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Detect which IDE configurations exist in the project
|
|
40
|
+
*
|
|
41
|
+
* @param projectPath - Path to the project root
|
|
42
|
+
* @returns Array of detected IDE types
|
|
43
|
+
*/
|
|
44
|
+
export function detectIDEConfigs(projectPath: string): IDEType[] {
|
|
45
|
+
const detected: IDEType[] = []
|
|
46
|
+
|
|
47
|
+
// Check for Cursor
|
|
48
|
+
if (existsSync(join(projectPath, '.cursor'))) {
|
|
49
|
+
detected.push('cursor')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check for Windsurf rules
|
|
53
|
+
if (existsSync(join(projectPath, '.windsurfrules'))) {
|
|
54
|
+
detected.push('windsurf')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for Claude Code
|
|
58
|
+
if (existsSync(join(projectPath, 'CLAUDE.md'))) {
|
|
59
|
+
detected.push('claude-code')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return detected
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Write an IDE-specific file
|
|
67
|
+
*
|
|
68
|
+
* @param projectPath - Path to the project root
|
|
69
|
+
* @param relativePath - Relative path for the file
|
|
70
|
+
* @param content - Content to write
|
|
71
|
+
* @returns Result object
|
|
72
|
+
*/
|
|
73
|
+
export function writeIDEFile(
|
|
74
|
+
projectPath: string,
|
|
75
|
+
relativePath: string,
|
|
76
|
+
content: string
|
|
77
|
+
): WriteIDEFileResult {
|
|
78
|
+
const fullPath = join(projectPath, relativePath)
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Ensure directory exists
|
|
82
|
+
const dir = dirname(fullPath)
|
|
83
|
+
if (!existsSync(dir)) {
|
|
84
|
+
mkdirSync(dir, { recursive: true })
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Write file
|
|
88
|
+
writeFileSync(fullPath, content)
|
|
89
|
+
|
|
90
|
+
return { success: true, path: fullPath }
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
path: fullPath,
|
|
95
|
+
error: `Failed to write ${relativePath}: ${err instanceof Error ? err.message : 'Unknown error'}`,
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Update the Oculum section in CLAUDE.md
|
|
102
|
+
* Replaces content between OCULUM_SECURITY_START and OCULUM_SECURITY_END markers
|
|
103
|
+
* If markers don't exist, appends the section at the end
|
|
104
|
+
*
|
|
105
|
+
* @param projectPath - Path to the project root
|
|
106
|
+
* @param section - The section content (including markers)
|
|
107
|
+
* @returns Result object
|
|
108
|
+
*/
|
|
109
|
+
export function updateClaudeMdSection(
|
|
110
|
+
projectPath: string,
|
|
111
|
+
section: string
|
|
112
|
+
): WriteIDEFileResult {
|
|
113
|
+
const filePath = join(projectPath, 'CLAUDE.md')
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
let content: string
|
|
117
|
+
|
|
118
|
+
if (existsSync(filePath)) {
|
|
119
|
+
content = readFileSync(filePath, 'utf-8')
|
|
120
|
+
|
|
121
|
+
// Check if markers exist
|
|
122
|
+
const startMarker = '<!-- OCULUM_SECURITY_START -->'
|
|
123
|
+
const endMarker = '<!-- OCULUM_SECURITY_END -->'
|
|
124
|
+
const startIndex = content.indexOf(startMarker)
|
|
125
|
+
const endIndex = content.indexOf(endMarker)
|
|
126
|
+
|
|
127
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
128
|
+
// Replace existing section
|
|
129
|
+
content =
|
|
130
|
+
content.substring(0, startIndex) +
|
|
131
|
+
section +
|
|
132
|
+
content.substring(endIndex + endMarker.length)
|
|
133
|
+
} else {
|
|
134
|
+
// Append section at end
|
|
135
|
+
content = content.trimEnd() + '\n\n' + section
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// Create new file with section
|
|
139
|
+
content = `# CLAUDE.md\n\n${section}`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
writeFileSync(filePath, content)
|
|
143
|
+
|
|
144
|
+
return { success: true, path: filePath }
|
|
145
|
+
} catch (err) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
path: filePath,
|
|
149
|
+
error: `Failed to update CLAUDE.md: ${err instanceof Error ? err.message : 'Unknown error'}`,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Clear IDE rule files
|
|
156
|
+
*
|
|
157
|
+
* @param projectPath - Path to the project root
|
|
158
|
+
* @param types - Optional array of IDE types to clear. If not provided, clears all.
|
|
159
|
+
* @returns Result object with list of cleared files
|
|
160
|
+
*/
|
|
161
|
+
export function clearIDEFiles(
|
|
162
|
+
projectPath: string,
|
|
163
|
+
types?: IDEType[]
|
|
164
|
+
): ClearIDEFilesResult {
|
|
165
|
+
const toCheck = types || (['cursor', 'windsurf', 'claude-code'] as IDEType[])
|
|
166
|
+
const clearedFiles: string[] = []
|
|
167
|
+
const errors: string[] = []
|
|
168
|
+
|
|
169
|
+
for (const ide of toCheck) {
|
|
170
|
+
const relativePath = IDE_FILE_PATHS[ide]
|
|
171
|
+
const fullPath = join(projectPath, relativePath)
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
if (ide === 'claude-code') {
|
|
175
|
+
// For CLAUDE.md, remove only the Oculum section, not the whole file
|
|
176
|
+
if (existsSync(fullPath)) {
|
|
177
|
+
const content = readFileSync(fullPath, 'utf-8')
|
|
178
|
+
const startMarker = '<!-- OCULUM_SECURITY_START -->'
|
|
179
|
+
const endMarker = '<!-- OCULUM_SECURITY_END -->'
|
|
180
|
+
const startIndex = content.indexOf(startMarker)
|
|
181
|
+
const endIndex = content.indexOf(endMarker)
|
|
182
|
+
|
|
183
|
+
if (startIndex !== -1 && endIndex !== -1) {
|
|
184
|
+
// Remove section (including potential surrounding newlines)
|
|
185
|
+
let newContent = content.substring(0, startIndex) + content.substring(endIndex + endMarker.length)
|
|
186
|
+
// Clean up extra newlines
|
|
187
|
+
newContent = newContent.replace(/\n{3,}/g, '\n\n').trimEnd() + '\n'
|
|
188
|
+
writeFileSync(fullPath, newContent)
|
|
189
|
+
clearedFiles.push(relativePath)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
// For other IDEs, delete the file
|
|
194
|
+
if (existsSync(fullPath)) {
|
|
195
|
+
unlinkSync(fullPath)
|
|
196
|
+
clearedFiles.push(relativePath)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch (err) {
|
|
200
|
+
errors.push(`Failed to clear ${relativePath}: ${err instanceof Error ? err.message : 'Unknown error'}`)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
success: errors.length === 0,
|
|
206
|
+
clearedFiles,
|
|
207
|
+
errors,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get the default file path for an IDE type
|
|
213
|
+
*/
|
|
214
|
+
export function getIDEFilePath(ide: IDEType): string {
|
|
215
|
+
return IDE_FILE_PATHS[ide]
|
|
216
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windsurf IDE Integration
|
|
3
|
+
* Generates .windsurfrules format
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ScanResult, Vulnerability, VulnerabilityCategory } from '../../types'
|
|
7
|
+
import { sortBySeverity } from '../grouping'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get security pattern rule for a category
|
|
11
|
+
*/
|
|
12
|
+
function getPatternRule(category: VulnerabilityCategory): string | null {
|
|
13
|
+
const rules: Partial<Record<VulnerabilityCategory, string>> = {
|
|
14
|
+
sql_injection: 'ALWAYS use parameterized queries for database operations.',
|
|
15
|
+
xss: 'ALWAYS escape user input before inserting into HTML.',
|
|
16
|
+
hardcoded_secret: 'NEVER hardcode secrets, API keys, or credentials in source code.',
|
|
17
|
+
high_entropy_string: 'NEVER commit high-entropy strings that could be secrets.',
|
|
18
|
+
missing_auth: 'ALWAYS add authentication middleware to API endpoints.',
|
|
19
|
+
dangerous_function: 'NEVER use eval(), exec(), or similar functions with user input.',
|
|
20
|
+
command_injection: 'ALWAYS sanitize input before passing to shell commands.',
|
|
21
|
+
sensitive_url: 'NEVER hardcode localhost or internal URLs in production code.',
|
|
22
|
+
insecure_config: 'ALWAYS review configuration files for security issues.',
|
|
23
|
+
data_exposure: 'NEVER log or expose sensitive data in error messages.',
|
|
24
|
+
weak_crypto: 'ALWAYS use strong cryptographic algorithms (avoid MD5, SHA1).',
|
|
25
|
+
ai_prompt_injection: 'ALWAYS sanitize user input before including in AI prompts.',
|
|
26
|
+
ai_unsafe_execution: 'NEVER execute code generated by AI without validation.',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return rules[category] || null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Format scan result as Windsurf rules
|
|
34
|
+
*
|
|
35
|
+
* @param result - The scan result to format
|
|
36
|
+
* @returns Markdown string for .windsurfrules
|
|
37
|
+
*/
|
|
38
|
+
export function formatWindsurfRules(result: ScanResult): string {
|
|
39
|
+
const { vulnerabilities, timestamp } = result
|
|
40
|
+
|
|
41
|
+
// Sort by severity
|
|
42
|
+
const sorted = sortBySeverity(vulnerabilities)
|
|
43
|
+
|
|
44
|
+
let md = ''
|
|
45
|
+
|
|
46
|
+
// Header
|
|
47
|
+
md += `# Oculum Security Rules\n\n`
|
|
48
|
+
md += `> Generated: ${timestamp}\n\n`
|
|
49
|
+
|
|
50
|
+
// No findings case
|
|
51
|
+
if (vulnerabilities.length === 0) {
|
|
52
|
+
md += `## Status\n\n`
|
|
53
|
+
md += `No security issues found.\n\n`
|
|
54
|
+
md += `*Run \`oculum scan --windsurf --clear\` to remove this file.*\n`
|
|
55
|
+
return md
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Security Issues Section
|
|
59
|
+
md += `## Security Issues\n\n`
|
|
60
|
+
md += `The following security issues were detected and need attention:\n\n`
|
|
61
|
+
|
|
62
|
+
// Group by file
|
|
63
|
+
const byFile = new Map<string, Vulnerability[]>()
|
|
64
|
+
for (const vuln of sorted) {
|
|
65
|
+
const group = byFile.get(vuln.filePath) || []
|
|
66
|
+
group.push(vuln)
|
|
67
|
+
byFile.set(vuln.filePath, group)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// List by file
|
|
71
|
+
for (const [filePath, vulns] of byFile) {
|
|
72
|
+
md += `### ${filePath}\n\n`
|
|
73
|
+
|
|
74
|
+
for (const vuln of vulns) {
|
|
75
|
+
const severityEmoji =
|
|
76
|
+
vuln.severity === 'critical' ? '!!' :
|
|
77
|
+
vuln.severity === 'high' ? '!' :
|
|
78
|
+
vuln.severity === 'medium' ? '*' : '-'
|
|
79
|
+
|
|
80
|
+
md += `- ${severityEmoji} **Line ${vuln.lineNumber}:** ${vuln.title}`
|
|
81
|
+
if (vuln.suggestedFix) {
|
|
82
|
+
md += ` - ${vuln.suggestedFix}`
|
|
83
|
+
}
|
|
84
|
+
md += '\n'
|
|
85
|
+
}
|
|
86
|
+
md += '\n'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Code Patterns Section
|
|
90
|
+
md += `## Code Patterns\n\n`
|
|
91
|
+
md += `Follow these security patterns when writing code:\n\n`
|
|
92
|
+
|
|
93
|
+
// Collect unique pattern rules
|
|
94
|
+
const patternRules = new Set<string>()
|
|
95
|
+
for (const vuln of vulnerabilities) {
|
|
96
|
+
const rule = getPatternRule(vuln.category)
|
|
97
|
+
if (rule) {
|
|
98
|
+
patternRules.add(rule)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const rule of patternRules) {
|
|
103
|
+
md += `- ${rule}\n`
|
|
104
|
+
}
|
|
105
|
+
md += '\n'
|
|
106
|
+
|
|
107
|
+
// Quick Reference
|
|
108
|
+
md += `## Quick Reference\n\n`
|
|
109
|
+
md += `| Severity | Count | Action |\n`
|
|
110
|
+
md += `|----------|-------|--------|\n`
|
|
111
|
+
|
|
112
|
+
const { severityCounts } = result
|
|
113
|
+
if (severityCounts.critical > 0) {
|
|
114
|
+
md += `| Critical | ${severityCounts.critical} | Fix immediately |\n`
|
|
115
|
+
}
|
|
116
|
+
if (severityCounts.high > 0) {
|
|
117
|
+
md += `| High | ${severityCounts.high} | Fix before commit |\n`
|
|
118
|
+
}
|
|
119
|
+
if (severityCounts.medium > 0) {
|
|
120
|
+
md += `| Medium | ${severityCounts.medium} | Review and fix |\n`
|
|
121
|
+
}
|
|
122
|
+
if (severityCounts.low > 0) {
|
|
123
|
+
md += `| Low | ${severityCounts.low} | Consider fixing |\n`
|
|
124
|
+
}
|
|
125
|
+
if (severityCounts.info > 0) {
|
|
126
|
+
md += `| Info | ${severityCounts.info} | For awareness |\n`
|
|
127
|
+
}
|
|
128
|
+
md += '\n'
|
|
129
|
+
|
|
130
|
+
// Footer
|
|
131
|
+
md += `---\n\n`
|
|
132
|
+
md += `*Run \`oculum scan\` to verify fixes. Run \`oculum scan --windsurf --clear\` to remove this file.*\n`
|
|
133
|
+
|
|
134
|
+
return md
|
|
135
|
+
}
|
package/src/formatters/index.ts
CHANGED
|
@@ -16,6 +16,30 @@ export {
|
|
|
16
16
|
type GroupedFindings,
|
|
17
17
|
} from './grouping'
|
|
18
18
|
|
|
19
|
+
// AI Context formatter
|
|
20
|
+
export {
|
|
21
|
+
formatAIContext,
|
|
22
|
+
type AIContextOptions,
|
|
23
|
+
} from './ai-context'
|
|
24
|
+
|
|
25
|
+
// IDE integrations
|
|
26
|
+
export {
|
|
27
|
+
formatCursorRules,
|
|
28
|
+
formatWindsurfRules,
|
|
29
|
+
formatClaudeCodeSection,
|
|
30
|
+
detectIDEConfigs,
|
|
31
|
+
writeIDEFile,
|
|
32
|
+
updateClaudeMdSection,
|
|
33
|
+
clearIDEFiles,
|
|
34
|
+
getIDEFilePath,
|
|
35
|
+
OCULUM_SECTION_START,
|
|
36
|
+
OCULUM_SECTION_END,
|
|
37
|
+
IDE_FILE_PATHS,
|
|
38
|
+
type IDEType,
|
|
39
|
+
type WriteIDEFileResult,
|
|
40
|
+
type ClearIDEFilesResult,
|
|
41
|
+
} from './ide'
|
|
42
|
+
|
|
19
43
|
// GitHub comment formatter
|
|
20
44
|
export {
|
|
21
45
|
formatGitHubComment,
|