@cencori/scan 0.3.3 → 0.3.5

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 CHANGED
@@ -1,85 +1,171 @@
1
1
  # @cencori/scan
2
2
 
3
- Security scanner for AI apps. Detect hardcoded secrets, PII leaks, and exposed routes.
3
+ **Security scanner for AI apps.** Detect hardcoded secrets, PII leaks, exposed routes, and security vulnerabilities — with AI-powered auto-fix.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@cencori/scan.svg)](https://www.npmjs.com/package/@cencori/scan)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Quick Start
9
+
10
+ ```bash
11
+ npx @cencori/scan
12
+ ```
13
+
14
+ That's it. Run it in any project directory to instantly scan for security issues.
15
+
16
+ ## Features
17
+
18
+ - 🔍 **Pattern-based scanning** - Detects 50+ types of secrets, PII, and vulnerabilities
19
+ - 🤖 **AI-powered auto-fix** - Automatically fixes issues with one command
20
+ - ⚡ **Fast** - Scans thousands of files in seconds
21
+ - 🎯 **Zero config** - Works out of the box
22
+ - 📊 **Security scoring** - A through F tier grading
4
23
 
5
24
  ## Installation
6
25
 
7
26
  ```bash
8
- # Run directly with npx
27
+ # Run directly (recommended)
9
28
  npx @cencori/scan
10
29
 
11
30
  # Or install globally
12
31
  npm install -g @cencori/scan
32
+
33
+ # Or as a dev dependency
34
+ npm install -D @cencori/scan
13
35
  ```
14
36
 
15
37
  ## Usage
16
38
 
39
+ ### Basic Scan
40
+
17
41
  ```bash
18
42
  # Scan current directory
19
- cencori-scan
43
+ npx @cencori/scan
20
44
 
21
45
  # Scan specific path
22
- cencori-scan ./my-project
46
+ npx @cencori/scan ./my-project
23
47
 
24
- # Output JSON
25
- cencori-scan --json
48
+ # Output JSON (for CI/CD)
49
+ npx @cencori/scan --json
26
50
 
27
51
  # Quiet mode (score only)
28
- cencori-scan --quiet
52
+ npx @cencori/scan --quiet
53
+
54
+ # Skip interactive prompts
55
+ npx @cencori/scan --no-prompt
56
+ ```
57
+
58
+ ### AI Auto-Fix (Pro)
59
+
60
+ After scanning, you'll be prompted:
61
+
62
+ ```
63
+ ? Would you like Cencori to auto-fix these issues? (y/n)
64
+ ```
65
+
66
+ Enter `y` and you'll be asked for your API key (if not already saved):
67
+
68
+ ```
69
+ ? Enter your Cencori API key: ************************
29
70
  ```
30
71
 
72
+ The AI will:
73
+ 1. Analyze each issue for false positives
74
+ 2. Generate secure code fixes
75
+ 3. Apply fixes automatically
76
+
77
+ Your API key is saved to `~/.cencorirc` for future scans.
78
+
79
+ **Get your free API key at [cencori.com/dashboard](https://cencori.com/dashboard)**
80
+
31
81
  ## What It Detects
32
82
 
33
- ### API Keys & Secrets
34
- - OpenAI, Anthropic, Google AI
35
- - Supabase, Firebase
36
- - Stripe, AWS, GitHub
37
- - And 20+ more providers
83
+ ### 🔐 API Keys & Secrets
84
+
85
+ | Provider | Pattern |
86
+ |----------|---------|
87
+ | OpenAI | `sk-...`, `sk-proj-...` |
88
+ | Anthropic | `sk-ant-...` |
89
+ | Google AI | `AIza...` |
90
+ | Supabase | `eyJh...` (service role) |
91
+ | Stripe | `sk_live_...`, `sk_test_...` |
92
+ | AWS | `AKIA...` |
93
+ | GitHub | `ghp_...`, `gho_...` |
94
+ | Firebase | `firebase-adminsdk-...` |
95
+ | And 20+ more... | |
38
96
 
39
- ### PII (Personal Identifiable Information)
40
- - Email addresses
97
+ ### 👤 PII (Personal Identifiable Information)
98
+
99
+ - Email addresses in code
41
100
  - Phone numbers
42
101
  - Social Security Numbers
43
102
  - Credit card numbers
44
103
 
45
- ### Exposed Routes
46
- - Next.js API routes without auth
47
- - Express routes without middleware
48
- - Sensitive files in public folders
104
+ ### 🛣️ Exposed Routes
105
+
106
+ - Next.js API routes without authentication
107
+ - Express routes without auth middleware
108
+ - Sensitive files in `/public` folders
109
+ - Dashboard/admin routes without protection
110
+
111
+ ### ⚠️ Security Vulnerabilities
112
+
113
+ - SQL injection patterns
114
+ - XSS vulnerabilities (innerHTML, dangerouslySetInnerHTML)
115
+ - Insecure CORS configuration (`Access-Control-Allow-Origin: *`)
116
+ - Hardcoded passwords
117
+ - Debug modes in production
49
118
 
50
119
  ## Security Score
51
120
 
52
- | Score | Meaning |
53
- |-------|---------|
54
- | A-Tier | Excellent - No issues found |
55
- | B-Tier | Good - Minor improvements needed |
56
- | C-Tier | Fair - Some concerns |
57
- | D-Tier | Poor - Significant issues |
58
- | F-Tier | Critical - Leaking secrets |
121
+ | Score | Meaning | Action Required |
122
+ |-------|---------|-----------------|
123
+ | **A-Tier** | Excellent | No security issues detected |
124
+ | **B-Tier** | Good | Minor improvements recommended |
125
+ | **C-Tier** | Fair | Some concerns need attention |
126
+ | **D-Tier** | Poor | Significant issues found |
127
+ | **F-Tier** | Critical | Secrets or major vulnerabilities exposed |
59
128
 
60
129
  ## Example Output
61
130
 
62
131
  ```
63
- Cencori Scan
64
- v0.1.0
132
+ Cencori Scan
133
+ v0.3.4
134
+
135
+ ✔ Scanned 142 files
136
+
137
+ ┌─────────────────────────────────────────────┐
138
+ │ Security Score: D-Tier │
139
+ └─────────────────────────────────────────────┘
65
140
 
66
- Scanned 142 files
141
+ Poor! Significant security issues found.
67
142
 
68
- ┌─────────────────────────────────────────────┐
69
- │ Security Score: F-Tier │
70
- └─────────────────────────────────────────────┘
143
+ SECRETS (3)
144
+ ├─ src/api.ts:12 sk-proj-****
145
+ │ Hardcoded API key - use environment variables
146
+ ├─ src/lib.ts:5 eyJh****
147
+ │ Supabase service role key exposed
148
+ └─ .env.local:3 ANTH****
149
+ Anthropic API key in tracked file
71
150
 
72
- SECRETS (3)
73
- ├─ src/api.ts:12 sk-proj-****
74
- ├─ src/lib.ts:5 eyJh****
75
- └─ .env.local:3 ANTH****
151
+ VULNERABILITIES (2)
152
+ ├─ src/db.ts:45 `SELECT * FROM users WHERE id = ${userId}`
153
+ │ Potential SQL injection - use parameterized queries
154
+ └─ src/page.tsx:23 dangerouslySetInnerHTML={{ __html: content }}
155
+ XSS vulnerability - sanitize content first
76
156
 
77
- Recommendations:
78
- - Use environment variables for secrets
79
- - Never commit API keys to version control
157
+ ─────────────────────────────────────────────
80
158
 
81
- Share: https://scan.cencori.com
82
- Docs: https://cencori.com/docs
159
+ Summary
160
+ Files scanned: 142
161
+ Scan time: 89ms
162
+
163
+ Recommendations:
164
+ - Use environment variables for secrets
165
+ - Never commit API keys to version control
166
+ - Sanitize user input before rendering HTML
167
+
168
+ ? Would you like Cencori to auto-fix these issues? (y/n)
83
169
  ```
84
170
 
85
171
  ## Programmatic Usage
@@ -89,10 +175,106 @@ import { scan } from '@cencori/scan';
89
175
 
90
176
  const result = await scan('./my-project');
91
177
 
92
- console.log(result.score); // 'A' | 'B' | 'C' | 'D' | 'F'
93
- console.log(result.issues); // Array of detected issues
178
+ console.log(result.score); // 'A' | 'B' | 'C' | 'D' | 'F'
179
+ console.log(result.issues); // Array of detected issues
180
+ console.log(result.filesScanned); // Number of files scanned
181
+ console.log(result.scanDuration); // Time in milliseconds
182
+ ```
183
+
184
+ ### TypeScript Types
185
+
186
+ ```typescript
187
+ interface ScanResult {
188
+ score: 'A' | 'B' | 'C' | 'D' | 'F';
189
+ tierDescription: string;
190
+ issues: ScanIssue[];
191
+ filesScanned: number;
192
+ scanDuration: number;
193
+ summary: {
194
+ critical: number;
195
+ high: number;
196
+ medium: number;
197
+ low: number;
198
+ };
199
+ }
200
+
201
+ interface ScanIssue {
202
+ type: 'secret' | 'pii' | 'route' | 'config' | 'vulnerability';
203
+ severity: 'critical' | 'high' | 'medium' | 'low';
204
+ name: string;
205
+ match: string;
206
+ file: string;
207
+ line: number;
208
+ description?: string;
209
+ }
210
+ ```
211
+
212
+ ## CI/CD Integration
213
+
214
+ ### GitHub Actions
215
+
216
+ ```yaml
217
+ name: Security Scan
218
+
219
+ on: [push, pull_request]
220
+
221
+ jobs:
222
+ scan:
223
+ runs-on: ubuntu-latest
224
+ steps:
225
+ - uses: actions/checkout@v4
226
+ - name: Run Cencori Scan
227
+ run: npx @cencori/scan --json > scan-results.json
228
+ - name: Check for failures
229
+ run: |
230
+ SCORE=$(jq -r '.score' scan-results.json)
231
+ if [[ "$SCORE" == "F" ]]; then
232
+ echo "Security scan failed with F-Tier score"
233
+ exit 1
234
+ fi
94
235
  ```
95
236
 
237
+ ### Pre-commit Hook
238
+
239
+ Add to `.husky/pre-commit`:
240
+
241
+ ```bash
242
+ #!/bin/sh
243
+ npx @cencori/scan --quiet --no-prompt
244
+ ```
245
+
246
+ ## Configuration
247
+
248
+ ### Environment Variables
249
+
250
+ | Variable | Description |
251
+ |----------|-------------|
252
+ | `CENCORI_API_KEY` | API key for AI features (optional) |
253
+
254
+ ### Config File
255
+
256
+ API keys are automatically saved to `~/.cencorirc`:
257
+
258
+ ```
259
+ api_key=your_cencori_api_key
260
+ ```
261
+
262
+ ## Privacy
263
+
264
+ Cencori Scan collects **anonymous usage metrics** to improve the product:
265
+ - Number of files scanned
266
+ - Number of issues found
267
+ - Security score
268
+ - Platform (macOS/Linux/Windows)
269
+
270
+ **No code, file paths, or sensitive data is ever transmitted.**
271
+
272
+ ## Links
273
+
274
+ - **Documentation**: [cencori.com/docs](https://cencori.com/docs)
275
+ - **Dashboard**: [cencori.com/dashboard](https://cencori.com/dashboard)
276
+ - **Web Scanner**: [scan.cencori.com](https://scan.cencori.com)
277
+
96
278
  ## License
97
279
 
98
- MIT - Cencori
280
+ MIT - [Cencori](https://cencori.com)
package/dist/cli.js CHANGED
@@ -925,10 +925,58 @@ async function applyFixes(fixes, fileContents) {
925
925
  return fixes;
926
926
  }
927
927
 
928
+ // src/telemetry.ts
929
+ var TELEMETRY_URL = "https://cencori.com/api/v1/telemetry/scan";
930
+ var pendingTelemetry = null;
931
+ function sendTelemetry(data) {
932
+ pendingTelemetry = fetch(TELEMETRY_URL, {
933
+ method: "POST",
934
+ headers: {
935
+ "Content-Type": "application/json"
936
+ },
937
+ body: JSON.stringify(data)
938
+ }).then(() => {
939
+ }).catch(() => {
940
+ });
941
+ return pendingTelemetry;
942
+ }
943
+ async function flushTelemetry() {
944
+ if (pendingTelemetry) {
945
+ await pendingTelemetry;
946
+ pendingTelemetry = null;
947
+ }
948
+ }
949
+ function buildTelemetryData(result, version, hasApiKey) {
950
+ const breakdown = {
951
+ secrets: 0,
952
+ pii: 0,
953
+ routes: 0,
954
+ config: 0,
955
+ vulnerabilities: 0
956
+ };
957
+ for (const issue of result.issues) {
958
+ const type = issue.type;
959
+ if (type in breakdown) {
960
+ breakdown[type]++;
961
+ }
962
+ }
963
+ return {
964
+ event: "scan_completed",
965
+ version,
966
+ platform: process.platform,
967
+ filesScanned: result.filesScanned,
968
+ issuesFound: result.issues.length,
969
+ score: result.score,
970
+ hasApiKey,
971
+ scanDuration: result.scanDuration,
972
+ issueBreakdown: breakdown
973
+ };
974
+ }
975
+
928
976
  // src/cli.ts
929
977
  var fs3 = __toESM(require("fs"));
930
978
  var path3 = __toESM(require("path"));
931
- var VERSION = "0.3.3";
979
+ var VERSION = "0.3.4";
932
980
  var scoreStyles = {
933
981
  A: { color: import_chalk.default.green },
934
982
  B: { color: import_chalk.default.blue },
@@ -1192,7 +1240,9 @@ async function main() {
1192
1240
  import_commander.program.name("cencori-scan").description("Security scanner for AI apps. Detect secrets, PII, and exposed routes.").version(VERSION).argument("[path]", "Path to scan", ".").option("-j, --json", "Output results as JSON").option("-q, --quiet", "Only output the score").option("--no-prompt", "Skip interactive prompts").option("--no-color", "Disable colored output").action(async (targetPath, options) => {
1193
1241
  if (options.json) {
1194
1242
  const result = await scan(targetPath);
1243
+ sendTelemetry(buildTelemetryData(result, VERSION, !!getApiKey()));
1195
1244
  console.log(JSON.stringify(result, null, 2));
1245
+ await flushTelemetry();
1196
1246
  process.exit(result.score === "A" || result.score === "B" ? 0 : 1);
1197
1247
  return;
1198
1248
  }
@@ -1203,12 +1253,14 @@ async function main() {
1203
1253
  }).start();
1204
1254
  try {
1205
1255
  const result = await scan(targetPath);
1256
+ sendTelemetry(buildTelemetryData(result, VERSION, !!getApiKey()));
1206
1257
  spinner.succeed(`Scanned ${result.filesScanned} files`);
1207
1258
  if (options.quiet) {
1208
1259
  const style = scoreStyles[result.score];
1209
1260
  console.log(`
1210
1261
  Score: ${style.color.bold(result.score + "-Tier")}
1211
1262
  `);
1263
+ await flushTelemetry();
1212
1264
  process.exit(result.score === "A" || result.score === "B" ? 0 : 1);
1213
1265
  return;
1214
1266
  }
@@ -1220,11 +1272,13 @@ async function main() {
1220
1272
  await handleAutoFix(result, targetPath);
1221
1273
  }
1222
1274
  printFooter();
1275
+ await flushTelemetry();
1223
1276
  process.exit(result.score === "A" || result.score === "B" ? 0 : 1);
1224
1277
  } catch (error) {
1225
1278
  spinner.fail("Scan failed");
1226
1279
  console.error(import_chalk.default.red(`
1227
1280
  Error: ${error instanceof Error ? error.message : "Unknown error"}`));
1281
+ await flushTelemetry();
1228
1282
  process.exit(1);
1229
1283
  }
1230
1284
  });