sertivibed 0.1.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 ADDED
@@ -0,0 +1,209 @@
1
+ # Sertivibed
2
+
3
+ **Evidence-first security scanner for Supabase/React apps.**
4
+
5
+ Sertivibed scans your codebase for common security issues and generates detailed reports with code evidence. No AI guessing — every finding is backed by actual code snippets.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ # Scan current directory
11
+ npx sertivibed scan .
12
+
13
+ # Scan a specific project
14
+ npx sertivibed scan ./my-app
15
+
16
+ # Show detailed progress
17
+ npx sertivibed scan . --verbose
18
+
19
+ # Fail CI if HIGH+ issues found
20
+ npx sertivibed scan . --fail-on high
21
+ ```
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ # Use with npx (no install needed)
27
+ npx sertivibed scan .
28
+
29
+ # Or install globally
30
+ npm install -g sertivibed
31
+ sertivibed scan .
32
+ ```
33
+
34
+ ## CLI Options
35
+
36
+ | Flag | Description |
37
+ |------|-------------|
38
+ | `--verbose`, `-v` | Show detailed progress |
39
+ | `--out <dir>` | Custom output directory (default: scanned project) |
40
+ | `--fail-on <severity>` | Exit code 1 if issues >= threshold (`critical`, `high`, `medium`, `low`) |
41
+ | `--json-only` | Only output JSON, skip markdown report |
42
+ | `--quiet` | Minimal terminal output |
43
+ | `--output <file>`, `-o` | Markdown report filename (default: `sertivibed-report.md`) |
44
+ | `--json <file>`, `-j` | JSON output filename (default: `sertivibed-results.json`) |
45
+
46
+ ## Security Rules (12)
47
+
48
+ ### RLS (Row Level Security)
49
+
50
+ | Rule | Severity | Description |
51
+ |------|----------|-------------|
52
+ | RLS001 | CRITICAL | RLS not enabled on table |
53
+ | RLS002 | MEDIUM | Missing DELETE policy when `.delete()` is used |
54
+ | RLS003 | HIGH | Policy missing ownership check (`auth.uid() = user_id`) |
55
+
56
+ ### Authentication & Authorization
57
+
58
+ | Rule | Severity | Description |
59
+ |------|----------|-------------|
60
+ | AUTH001 | CRITICAL | Service role key in client code |
61
+
62
+ ### Privacy
63
+
64
+ | Rule | Severity | Description |
65
+ |------|----------|-------------|
66
+ | PRIV001 | MEDIUM | Auth token in localStorage |
67
+ | PRIV002 | MEDIUM | PII in console.log |
68
+
69
+ ### Secrets
70
+
71
+ | Rule | Severity | Description |
72
+ |------|----------|-------------|
73
+ | SEC001 | HIGH | Hardcoded API key |
74
+
75
+ ### XSS (Cross-Site Scripting)
76
+
77
+ | Rule | Severity | Description |
78
+ |------|----------|-------------|
79
+ | XSS001 | HIGH | `dangerouslySetInnerHTML` usage |
80
+ | XSS002 | HIGH | Direct `innerHTML` assignment |
81
+
82
+ ### Storage
83
+
84
+ | Rule | Severity | Description |
85
+ |------|----------|-------------|
86
+ | STOR001 | MEDIUM | File upload without validation |
87
+ | STOR002 | MEDIUM | Public storage URL without signing |
88
+
89
+ ### Configuration
90
+
91
+ | Rule | Severity | Description |
92
+ |------|----------|-------------|
93
+ | CONF001 | LOW | Missing Content Security Policy |
94
+
95
+ ## Output
96
+
97
+ Sertivibed generates two files:
98
+
99
+ - **`sertivibed-report.md`** — Human-readable report with code evidence
100
+ - **`sertivibed-results.json`** — Machine-readable for CI/CD integration
101
+
102
+ ### Example Report
103
+
104
+ ```
105
+ SERTIVIBED SIKKERHETSRAPPORT
106
+ my-app
107
+ 10.1.2026
108
+
109
+ Totalvurdering: BESTATT
110
+
111
+ ## Coverage
112
+
113
+ Regler kjort:
114
+ RLS001, RLS002, RLS003, AUTH001, PRIV001, PRIV002, SEC001, XSS001, XSS002
115
+
116
+ ## Sammendrag
117
+
118
+ | Severity | Antall |
119
+ |-----------|--------|
120
+ | CRITICAL | 0 |
121
+ | HIGH | 0 |
122
+ | MEDIUM | 0 |
123
+ | LOW | 1 |
124
+ ```
125
+
126
+ ## CI/CD Integration
127
+
128
+ ### GitHub Actions
129
+
130
+ ```yaml
131
+ name: Security Scan
132
+
133
+ on: [push, pull_request]
134
+
135
+ jobs:
136
+ security:
137
+ runs-on: ubuntu-latest
138
+ steps:
139
+ - uses: actions/checkout@v4
140
+
141
+ - name: Run Sertivibed
142
+ run: npx sertivibed scan . --fail-on high --quiet
143
+
144
+ - name: Upload Report
145
+ if: always()
146
+ uses: actions/upload-artifact@v4
147
+ with:
148
+ name: security-report
149
+ path: sertivibed-report.md
150
+ ```
151
+
152
+ ### GitLab CI
153
+
154
+ ```yaml
155
+ security-scan:
156
+ image: node:20
157
+ script:
158
+ - npx sertivibed scan . --fail-on high
159
+ artifacts:
160
+ paths:
161
+ - sertivibed-report.md
162
+ when: always
163
+ ```
164
+
165
+ ## Philosophy
166
+
167
+ Sertivibed follows an **evidence-first** approach:
168
+
169
+ 1. **Every finding has code evidence** — No vague warnings. You see the exact file and line.
170
+ 2. **Verification steps included** — Each finding tells you how to manually verify it's real.
171
+ 3. **Smart detection** — Skips RLS checks for Prisma projects (they use different auth patterns).
172
+ 4. **No false confidence** — We only check what we can verify. Unknown stacks get honest "N/A".
173
+
174
+ ## Supported Stacks
175
+
176
+ | Stack | Detection | RLS Rules |
177
+ |-------|-----------|-----------|
178
+ | Supabase | Auto-detected | Full support |
179
+ | Prisma | Auto-detected | Skipped (check API routes instead) |
180
+ | Next.js | Auto-detected | Full support |
181
+ | Vite/React | Auto-detected | Full support |
182
+
183
+ ## Requirements
184
+
185
+ - Node.js >= 18
186
+ - ripgrep (rg) recommended for performance, falls back to grep
187
+
188
+ ### Install ripgrep
189
+
190
+ ```bash
191
+ # macOS
192
+ brew install ripgrep
193
+
194
+ # Ubuntu/Debian
195
+ sudo apt install ripgrep
196
+
197
+ # Windows
198
+ choco install ripgrep
199
+ # or
200
+ winget install BurntSushi.ripgrep.MSVC
201
+ ```
202
+
203
+ ## Contributing
204
+
205
+ Issues and PRs welcome at [github.com/sertivibed/sertivibed-cli](https://github.com/sertivibed/sertivibed-cli).
206
+
207
+ ## License
208
+
209
+ MIT - Hakon Haugen
package/dist/cli.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Sertivibed CLI
4
+ *
5
+ * Evidence-first security screening for Supabase/React apps.
6
+ *
7
+ * Usage:
8
+ * npx sertivibed scan # Scan current directory
9
+ * npx sertivibed scan ./my-app # Scan specific directory
10
+ * npx sertivibed scan --verbose # Show progress
11
+ * npx sertivibed scan --fail-on=high # Exit 1 if HIGH+ found
12
+ */
13
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Sertivibed CLI
5
+ *
6
+ * Evidence-first security screening for Supabase/React apps.
7
+ *
8
+ * Usage:
9
+ * npx sertivibed scan # Scan current directory
10
+ * npx sertivibed scan ./my-app # Scan specific directory
11
+ * npx sertivibed scan --verbose # Show progress
12
+ * npx sertivibed scan --fail-on=high # Exit 1 if HIGH+ found
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const commander_1 = require("commander");
16
+ const fs_1 = require("fs");
17
+ const path_1 = require("path");
18
+ const scan_1 = require("./scan");
19
+ const md_1 = require("./reporters/md");
20
+ const json_1 = require("./reporters/json");
21
+ const types_1 = require("./types");
22
+ // ============================================
23
+ // CLI SETUP
24
+ // ============================================
25
+ const program = new commander_1.Command();
26
+ program
27
+ .name("sertivibed")
28
+ .description("Evidence-first security screening for Supabase/React apps")
29
+ .version("0.1.0");
30
+ // ============================================
31
+ // SCAN COMMAND
32
+ // ============================================
33
+ program
34
+ .command("scan")
35
+ .description("Scan a project for security issues")
36
+ .argument("[path]", "Path to project (default: current directory)", ".")
37
+ .option("-v, --verbose", "Show detailed progress")
38
+ .option("-o, --output <file>", "Output markdown report to file", "sertivibed-report.md")
39
+ .option("-j, --json <file>", "Output JSON results to file", "sertivibed-results.json")
40
+ .option("--out <dir>", "Custom output directory (default: scanned project dir)")
41
+ .option("--fail-on <severity>", "Exit with code 1 if severity >= threshold (critical|high|medium|low)")
42
+ .option("--json-only", "Only output JSON, skip markdown report")
43
+ .option("--quiet", "Minimal terminal output")
44
+ .option("--no-report", "Skip generating markdown report")
45
+ .option("--no-json", "Skip generating JSON output")
46
+ .action(async (path, options) => {
47
+ const targetPath = (0, path_1.resolve)(path);
48
+ const quiet = options.quiet || false;
49
+ // Validate path exists
50
+ if (!(0, fs_1.existsSync)(targetPath)) {
51
+ console.error(`❌ Path not found: ${targetPath}`);
52
+ process.exit(1);
53
+ }
54
+ // Determine output directory
55
+ const outputDir = options.out ? (0, path_1.resolve)(options.out) : targetPath;
56
+ if (options.out && !(0, fs_1.existsSync)(outputDir)) {
57
+ (0, fs_1.mkdirSync)(outputDir, { recursive: true });
58
+ }
59
+ if (!quiet) {
60
+ console.log(`
61
+ ╔═══════════════════════════════════════════════════════════════╗
62
+ ║ SERTIVIBED SCANNER v0.1 ║
63
+ ║ Evidence-first Security Screening ║
64
+ ╚═══════════════════════════════════════════════════════════════╝
65
+ `);
66
+ }
67
+ // Determine what to output
68
+ const skipMarkdown = options.jsonOnly || !options.report;
69
+ const skipJson = !options.json;
70
+ const config = {
71
+ targetPath,
72
+ verbose: options.verbose && !quiet,
73
+ outputMd: skipMarkdown ? undefined : options.output,
74
+ outputJson: skipJson ? undefined : options.json,
75
+ failOn: options.failOn?.toUpperCase(),
76
+ quiet,
77
+ };
78
+ try {
79
+ // Run scan
80
+ if (!quiet) {
81
+ console.log(`📁 Scanning: ${targetPath}`);
82
+ console.log("");
83
+ }
84
+ const result = await (0, scan_1.scan)(config);
85
+ // Display summary
86
+ if (!quiet) {
87
+ console.log("═══════════════════════════════════════════════════════════════");
88
+ console.log(" SUMMARY ");
89
+ console.log("═══════════════════════════════════════════════════════════════");
90
+ console.log("");
91
+ console.log(` 🔴 Critical: ${result.summary.bySeverity.CRITICAL}`);
92
+ console.log(` 🟠 High: ${result.summary.bySeverity.HIGH}`);
93
+ console.log(` 🟡 Medium: ${result.summary.bySeverity.MEDIUM}`);
94
+ console.log(` 🟢 Low: ${result.summary.bySeverity.LOW}`);
95
+ console.log(` ⚪ Info: ${result.summary.bySeverity.INFO}`);
96
+ console.log("");
97
+ console.log(` Total issues: ${result.summary.total}`);
98
+ console.log(` Rules checked: ${result.rulesChecked.length}`);
99
+ console.log(` Scan time: ${result.meta.scanDurationMs}ms`);
100
+ console.log("");
101
+ }
102
+ // Generate outputs
103
+ if (config.outputJson) {
104
+ const jsonPath = (0, path_1.join)(outputDir, config.outputJson);
105
+ (0, fs_1.writeFileSync)(jsonPath, (0, json_1.generateJsonReport)(result));
106
+ if (!quiet)
107
+ console.log(`📄 JSON saved: ${config.outputJson}`);
108
+ }
109
+ if (config.outputMd) {
110
+ const mdPath = (0, path_1.join)(outputDir, config.outputMd);
111
+ (0, fs_1.writeFileSync)(mdPath, (0, md_1.generateMarkdownReport)(result));
112
+ if (!quiet)
113
+ console.log(`📋 Report saved: ${config.outputMd}`);
114
+ }
115
+ if (!quiet)
116
+ console.log("");
117
+ // Check fail threshold
118
+ if (config.failOn) {
119
+ const threshold = types_1.SEVERITY_ORDER[config.failOn];
120
+ const hasFailure = result.claims.some(c => types_1.SEVERITY_ORDER[c.severity] >= threshold);
121
+ if (hasFailure) {
122
+ if (!quiet)
123
+ console.log(`❌ Found issues at or above ${config.failOn} severity`);
124
+ process.exit(1);
125
+ }
126
+ }
127
+ // Final verdict
128
+ if (!quiet) {
129
+ const hasCritical = result.summary.bySeverity.CRITICAL > 0;
130
+ const hasHigh = result.summary.bySeverity.HIGH > 0;
131
+ if (hasCritical || hasHigh) {
132
+ console.log("⚠️ Issues found that should be addressed before production.");
133
+ }
134
+ else if (result.summary.total > 0) {
135
+ console.log("ℹ️ Some issues found. Review the report for details.");
136
+ }
137
+ else {
138
+ console.log("✅ No security issues found!");
139
+ }
140
+ }
141
+ }
142
+ catch (error) {
143
+ console.error(`❌ Scan failed: ${error}`);
144
+ process.exit(1);
145
+ }
146
+ });
147
+ // ============================================
148
+ // LIST RULES COMMAND
149
+ // ============================================
150
+ program
151
+ .command("rules")
152
+ .description("List all security rules")
153
+ .action(() => {
154
+ const { RULES } = require("./rules/index");
155
+ console.log("\nSertivibed Security Rules v0.1\n");
156
+ console.log("═══════════════════════════════════════════════════════════════\n");
157
+ for (const rule of RULES) {
158
+ console.log(`${rule.id}: ${rule.title}`);
159
+ console.log(` Category: ${rule.category} | Severity: ${rule.severity}`);
160
+ console.log(` ${rule.rationale}`);
161
+ console.log("");
162
+ }
163
+ });
164
+ // ============================================
165
+ // RUN
166
+ // ============================================
167
+ program.parse();
@@ -0,0 +1,12 @@
1
+ /**
2
+ * JSON Reporter
3
+ *
4
+ * Eksporterer scan-resultat som JSON for maskinlesing
5
+ * eller videre prosessering (f.eks. LLM enrichment).
6
+ */
7
+ import { ScanResult } from "../types";
8
+ export declare function generateJsonReport(result: ScanResult): string;
9
+ /**
10
+ * Kompakt versjon for API-kall / LLM
11
+ */
12
+ export declare function generateCompactJson(result: ScanResult): string;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * JSON Reporter
4
+ *
5
+ * Eksporterer scan-resultat som JSON for maskinlesing
6
+ * eller videre prosessering (f.eks. LLM enrichment).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.generateJsonReport = generateJsonReport;
10
+ exports.generateCompactJson = generateCompactJson;
11
+ function generateJsonReport(result) {
12
+ return JSON.stringify(result, null, 2);
13
+ }
14
+ /**
15
+ * Kompakt versjon for API-kall / LLM
16
+ */
17
+ function generateCompactJson(result) {
18
+ const compact = {
19
+ project: result.meta.projectName,
20
+ stack: result.meta.stack,
21
+ summary: {
22
+ total: result.summary.total,
23
+ critical: result.summary.bySeverity.CRITICAL,
24
+ high: result.summary.bySeverity.HIGH,
25
+ medium: result.summary.bySeverity.MEDIUM,
26
+ low: result.summary.bySeverity.LOW,
27
+ },
28
+ findings: result.claims.map(c => ({
29
+ id: c.ruleId,
30
+ title: c.title,
31
+ severity: c.severity,
32
+ impact: c.impactType,
33
+ statement: c.statement,
34
+ files: c.evidence.map(e => e.path),
35
+ fix: c.fixHint,
36
+ })),
37
+ };
38
+ return JSON.stringify(compact, null, 2);
39
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Markdown Reporter
3
+ *
4
+ * Genererer profesjonell rapport UTEN LLM.
5
+ * Alt er deterministisk og basert på claims + evidence.
6
+ */
7
+ import { ScanResult } from "../types";
8
+ export declare function generateMarkdownReport(result: ScanResult): string;