@soulbatical/tetra-dev-toolkit 1.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.
Files changed (39) hide show
  1. package/README.md +312 -0
  2. package/bin/vca-audit.js +90 -0
  3. package/bin/vca-dev-token.js +39 -0
  4. package/bin/vca-setup.js +227 -0
  5. package/lib/checks/codeQuality/api-response-format.js +268 -0
  6. package/lib/checks/health/claude-md.js +114 -0
  7. package/lib/checks/health/doppler-compliance.js +174 -0
  8. package/lib/checks/health/git.js +61 -0
  9. package/lib/checks/health/gitignore.js +83 -0
  10. package/lib/checks/health/index.js +26 -0
  11. package/lib/checks/health/infrastructure-yml.js +87 -0
  12. package/lib/checks/health/mcps.js +57 -0
  13. package/lib/checks/health/naming-conventions.js +302 -0
  14. package/lib/checks/health/plugins.js +38 -0
  15. package/lib/checks/health/quality-toolkit.js +97 -0
  16. package/lib/checks/health/repo-visibility.js +70 -0
  17. package/lib/checks/health/rls-audit.js +130 -0
  18. package/lib/checks/health/scanner.js +68 -0
  19. package/lib/checks/health/secrets.js +80 -0
  20. package/lib/checks/health/stella-integration.js +124 -0
  21. package/lib/checks/health/tests.js +140 -0
  22. package/lib/checks/health/types.js +77 -0
  23. package/lib/checks/health/vincifox-widget.js +47 -0
  24. package/lib/checks/index.js +17 -0
  25. package/lib/checks/security/deprecated-supabase-admin.js +96 -0
  26. package/lib/checks/security/gitignore-validation.js +211 -0
  27. package/lib/checks/security/hardcoded-secrets.js +95 -0
  28. package/lib/checks/security/service-key-exposure.js +107 -0
  29. package/lib/checks/security/systemdb-whitelist.js +138 -0
  30. package/lib/checks/stability/ci-pipeline.js +143 -0
  31. package/lib/checks/stability/husky-hooks.js +117 -0
  32. package/lib/checks/stability/npm-audit.js +140 -0
  33. package/lib/checks/supabase/rls-policy-audit.js +261 -0
  34. package/lib/commands/dev-token.js +342 -0
  35. package/lib/config.js +213 -0
  36. package/lib/index.js +17 -0
  37. package/lib/reporters/terminal.js +134 -0
  38. package/lib/runner.js +179 -0
  39. package/package.json +72 -0
package/lib/config.js ADDED
@@ -0,0 +1,213 @@
1
+ /**
2
+ * VCA Quality Toolkit - Configuration
3
+ *
4
+ * Default configuration that can be overridden per project via:
5
+ * - .vca-quality.json in project root
6
+ * - vca-quality key in package.json
7
+ */
8
+
9
+ import { readFileSync, existsSync } from 'fs'
10
+ import { join } from 'path'
11
+
12
+ export const DEFAULT_CONFIG = {
13
+ // Which check suites to run
14
+ suites: {
15
+ security: true,
16
+ stability: true,
17
+ codeQuality: true,
18
+ supabase: 'auto' // true, false, or 'auto' (detect)
19
+ },
20
+
21
+ // Security checks
22
+ security: {
23
+ // Supabase-specific
24
+ checkServiceKeyExposure: true,
25
+ checkRlsPolicies: true,
26
+ checkRpcSecurity: true,
27
+
28
+ // Secrets
29
+ checkHardcodedSecrets: true,
30
+ checkEnvInGitignore: true,
31
+ secretPatterns: [
32
+ 'sk-[a-zA-Z0-9]{20,}', // OpenAI
33
+ 'sk_live_[a-zA-Z0-9]{20,}', // Stripe
34
+ 'sk_test_[a-zA-Z0-9]{20,}', // Stripe test
35
+ 'AKIA[A-Z0-9]{16}', // AWS
36
+ 'ghp_[a-zA-Z0-9]{36}', // GitHub
37
+ 'xox[baprs]-[a-zA-Z0-9-]{10,}', // Slack
38
+ 'eyJ[a-zA-Z0-9_-]*\\.[a-zA-Z0-9_-]*\\.[a-zA-Z0-9_-]*' // JWT
39
+ ],
40
+
41
+ // Code patterns
42
+ checkSqlInjection: true,
43
+ checkEvalUsage: true,
44
+ checkCommandInjection: true
45
+ },
46
+
47
+ // Stability checks
48
+ stability: {
49
+ // Testing
50
+ requireTestFramework: true,
51
+ requireTestFiles: true,
52
+ minTestFiles: 1,
53
+ requireCoverage: false,
54
+ minCoverage: 0,
55
+
56
+ // Linting
57
+ requireEslint: true,
58
+ requireSecurityPlugin: true,
59
+
60
+ // Pre-commit
61
+ requireHusky: true,
62
+ requirePreCommitHook: true,
63
+
64
+ // CI/CD
65
+ requireCiConfig: true,
66
+ requireTestsInCi: true,
67
+ requireLintInCi: true,
68
+
69
+ // Dependencies
70
+ requireLockFile: true,
71
+ allowedVulnerabilities: {
72
+ critical: 0,
73
+ high: 0,
74
+ moderate: 10
75
+ }
76
+ },
77
+
78
+ // Code quality checks
79
+ codeQuality: {
80
+ // Architecture
81
+ maxFileLines: 500,
82
+ checkCircularDeps: true,
83
+
84
+ // Dead code
85
+ runKnip: true,
86
+
87
+ // TypeScript
88
+ checkAnyTypes: true,
89
+ maxAnyCount: 10
90
+ },
91
+
92
+ // Supabase-specific checks
93
+ supabase: {
94
+ checkRls: true,
95
+ checkSecurityDefiner: true,
96
+ checkViewSecurity: true,
97
+ checkEdgeFunctions: true,
98
+
99
+ // Whitelist for intentionally public RPC functions
100
+ publicRpcFunctions: [],
101
+
102
+ // Tables that intentionally have no RLS (e.g., public lookup tables)
103
+ publicTables: []
104
+ },
105
+
106
+ // Paths
107
+ paths: {
108
+ backend: ['backend/src', 'src'],
109
+ frontend: ['frontend/src', 'src'],
110
+ migrations: ['supabase/migrations', 'migrations'],
111
+ tests: ['**/*.test.ts', '**/*.spec.ts', '**/*.test.tsx', '**/*.spec.tsx']
112
+ },
113
+
114
+ // Ignore patterns
115
+ ignore: [
116
+ 'node_modules/**',
117
+ 'dist/**',
118
+ 'build/**',
119
+ '.next/**',
120
+ 'coverage/**',
121
+ '*.min.js'
122
+ ],
123
+
124
+ // Output
125
+ output: {
126
+ format: 'terminal', // 'terminal', 'json', 'github-actions'
127
+ verbose: false,
128
+ failOn: 'error' // 'error', 'warning', 'none'
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Load project-specific configuration
134
+ */
135
+ export function loadConfig(projectRoot = process.cwd()) {
136
+ let projectConfig = {}
137
+
138
+ // Check for .vca-quality.json
139
+ const configFile = join(projectRoot, '.vca-quality.json')
140
+ if (existsSync(configFile)) {
141
+ try {
142
+ projectConfig = JSON.parse(readFileSync(configFile, 'utf-8'))
143
+ } catch (e) {
144
+ console.warn(`Warning: Could not parse ${configFile}`)
145
+ }
146
+ }
147
+
148
+ // Check for vca-quality in package.json
149
+ const packageFile = join(projectRoot, 'package.json')
150
+ if (existsSync(packageFile)) {
151
+ try {
152
+ const pkg = JSON.parse(readFileSync(packageFile, 'utf-8'))
153
+ if (pkg['vca-quality']) {
154
+ projectConfig = { ...projectConfig, ...pkg['vca-quality'] }
155
+ }
156
+ } catch (e) {
157
+ // Ignore
158
+ }
159
+ }
160
+
161
+ // Deep merge with defaults
162
+ return deepMerge(DEFAULT_CONFIG, projectConfig)
163
+ }
164
+
165
+ /**
166
+ * Deep merge two objects
167
+ */
168
+ function deepMerge(target, source) {
169
+ const result = { ...target }
170
+
171
+ for (const key in source) {
172
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
173
+ result[key] = deepMerge(target[key] || {}, source[key])
174
+ } else {
175
+ result[key] = source[key]
176
+ }
177
+ }
178
+
179
+ return result
180
+ }
181
+
182
+ /**
183
+ * Detect if project uses Supabase
184
+ */
185
+ export function detectSupabase(projectRoot = process.cwd()) {
186
+ const indicators = [
187
+ 'supabase',
188
+ 'supabase/migrations',
189
+ 'supabase/functions'
190
+ ]
191
+
192
+ for (const indicator of indicators) {
193
+ if (existsSync(join(projectRoot, indicator))) {
194
+ return true
195
+ }
196
+ }
197
+
198
+ // Check package.json for supabase dependency
199
+ const packageFile = join(projectRoot, 'package.json')
200
+ if (existsSync(packageFile)) {
201
+ try {
202
+ const pkg = JSON.parse(readFileSync(packageFile, 'utf-8'))
203
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies }
204
+ if (deps['@supabase/supabase-js']) {
205
+ return true
206
+ }
207
+ } catch (e) {
208
+ // Ignore
209
+ }
210
+ }
211
+
212
+ return false
213
+ }
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * VCA Quality Toolkit
3
+ *
4
+ * Unified quality checks for all VCA projects.
5
+ * Consolidates security, stability, and code quality checks
6
+ * from sparkbuddy-live and vca-tools into a single npm package.
7
+ */
8
+
9
+ export { loadConfig, detectSupabase, DEFAULT_CONFIG } from './config.js'
10
+ export { runAllChecks, runSecurityChecks, runStabilityChecks, runQuickCheck } from './runner.js'
11
+ export { formatResults, formatGitHubActions } from './reporters/terminal.js'
12
+
13
+ // Re-export individual checks for advanced usage
14
+ export * as checks from './checks/index.js'
15
+
16
+ // Health scanner (project ecosystem checks)
17
+ export { scanProjectHealth, calculateHealthStatus } from './checks/health/index.js'
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Terminal Reporter - Pretty output for CLI
3
+ */
4
+
5
+ import chalk from 'chalk'
6
+
7
+ const SEVERITY_COLORS = {
8
+ critical: chalk.bgRed.white.bold,
9
+ high: chalk.red.bold,
10
+ medium: chalk.yellow,
11
+ low: chalk.blue
12
+ }
13
+
14
+ const SEVERITY_ICONS = {
15
+ critical: '🚨',
16
+ high: '❌',
17
+ medium: '⚠️',
18
+ low: 'ℹ️'
19
+ }
20
+
21
+ export function formatResults(results, options = {}) {
22
+ const lines = []
23
+
24
+ // Header
25
+ lines.push('')
26
+ lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
27
+ lines.push(chalk.bold.cyan(' 🔍 VCA Quality Toolkit - Audit Results'))
28
+ lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
29
+ lines.push('')
30
+
31
+ // Project info
32
+ lines.push(chalk.gray(` Project: ${results.projectRoot}`))
33
+ lines.push(chalk.gray(` Time: ${results.timestamp}`))
34
+ lines.push('')
35
+
36
+ // Summary box
37
+ const passed = results.passed
38
+ const statusIcon = passed ? '✅' : '❌'
39
+ const statusText = passed ? chalk.green.bold('PASSED') : chalk.red.bold('FAILED')
40
+
41
+ lines.push(chalk.bold(` ${statusIcon} Overall Status: ${statusText}`))
42
+ lines.push('')
43
+
44
+ // Finding counts
45
+ const { findings } = results.summary
46
+ if (findings.critical + findings.high + findings.medium + findings.low > 0) {
47
+ lines.push(chalk.bold(' Findings:'))
48
+ if (findings.critical > 0) lines.push(` ${SEVERITY_COLORS.critical(` ${findings.critical} CRITICAL `)}`)
49
+ if (findings.high > 0) lines.push(` ${SEVERITY_COLORS.high(`${findings.high} High`)}`)
50
+ if (findings.medium > 0) lines.push(` ${SEVERITY_COLORS.medium(`${findings.medium} Medium`)}`)
51
+ if (findings.low > 0) lines.push(` ${SEVERITY_COLORS.low(`${findings.low} Low`)}`)
52
+ lines.push('')
53
+ }
54
+
55
+ // Suite results
56
+ for (const [suiteName, suite] of Object.entries(results.suites || {})) {
57
+ const suiteIcon = suite.passed ? '✅' : '❌'
58
+ lines.push(chalk.bold(` ${suiteIcon} ${suiteName.toUpperCase()}`))
59
+ lines.push(chalk.gray(' ' + '─'.repeat(50)))
60
+
61
+ for (const check of suite.checks || []) {
62
+ const checkIcon = check.skipped ? '⏭️' : (check.passed ? '✅' : '❌')
63
+ const checkStatus = check.skipped
64
+ ? chalk.gray('SKIPPED')
65
+ : (check.passed ? chalk.green('PASS') : chalk.red('FAIL'))
66
+
67
+ lines.push(` ${checkIcon} ${check.name} ${checkStatus}`)
68
+
69
+ // Show findings for failed checks
70
+ if (!check.passed && !check.skipped && check.findings) {
71
+ for (const finding of check.findings.slice(0, options.maxFindings || 5)) {
72
+ const icon = SEVERITY_ICONS[finding.severity] || '•'
73
+ const color = SEVERITY_COLORS[finding.severity] || chalk.white
74
+
75
+ lines.push(color(` ${icon} ${finding.message}`))
76
+
77
+ if (finding.file) {
78
+ lines.push(chalk.gray(` ${finding.file}:${finding.line || '?'}`))
79
+ }
80
+
81
+ if (finding.fix && options.verbose) {
82
+ lines.push(chalk.cyan(` Fix: ${finding.fix}`))
83
+ }
84
+ }
85
+
86
+ if (check.findings.length > (options.maxFindings || 5)) {
87
+ lines.push(chalk.gray(` ... and ${check.findings.length - 5} more`))
88
+ }
89
+ }
90
+
91
+ // Show skip reason
92
+ if (check.skipped && check.skipReason) {
93
+ lines.push(chalk.gray(` (${check.skipReason})`))
94
+ }
95
+ }
96
+
97
+ lines.push('')
98
+ }
99
+
100
+ // Footer
101
+ lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
102
+ lines.push(` Checks: ${results.summary.passed} passed, ${results.summary.failed} failed, ${results.summary.skipped} skipped`)
103
+ lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
104
+ lines.push('')
105
+
106
+ return lines.join('\n')
107
+ }
108
+
109
+ /**
110
+ * Format for GitHub Actions annotations
111
+ */
112
+ export function formatGitHubActions(results) {
113
+ const annotations = []
114
+
115
+ for (const [, suite] of Object.entries(results.suites || {})) {
116
+ for (const check of suite.checks || []) {
117
+ if (!check.passed && !check.skipped && check.findings) {
118
+ for (const finding of check.findings) {
119
+ const level = finding.severity === 'critical' || finding.severity === 'high'
120
+ ? 'error'
121
+ : 'warning'
122
+
123
+ if (finding.file) {
124
+ annotations.push(`::${level} file=${finding.file},line=${finding.line || 1}::${finding.message}`)
125
+ } else {
126
+ annotations.push(`::${level}::${finding.message}`)
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ return annotations.join('\n')
134
+ }
package/lib/runner.js ADDED
@@ -0,0 +1,179 @@
1
+ /**
2
+ * VCA Quality Toolkit - Check Runner
3
+ *
4
+ * Orchestrates running all checks and collecting results
5
+ */
6
+
7
+ import { loadConfig, detectSupabase } from './config.js'
8
+
9
+ // Import all checks
10
+ import * as hardcodedSecrets from './checks/security/hardcoded-secrets.js'
11
+ import * as serviceKeyExposure from './checks/security/service-key-exposure.js'
12
+ import * as deprecatedSupabaseAdmin from './checks/security/deprecated-supabase-admin.js'
13
+ import * as systemdbWhitelist from './checks/security/systemdb-whitelist.js'
14
+ import * as huskyHooks from './checks/stability/husky-hooks.js'
15
+ import * as ciPipeline from './checks/stability/ci-pipeline.js'
16
+ import * as npmAudit from './checks/stability/npm-audit.js'
17
+ import * as apiResponseFormat from './checks/codeQuality/api-response-format.js'
18
+ import * as gitignoreValidation from './checks/security/gitignore-validation.js'
19
+ import * as rlsPolicyAudit from './checks/supabase/rls-policy-audit.js'
20
+
21
+ // Register all checks
22
+ const ALL_CHECKS = {
23
+ security: [
24
+ hardcodedSecrets,
25
+ serviceKeyExposure,
26
+ deprecatedSupabaseAdmin,
27
+ systemdbWhitelist,
28
+ gitignoreValidation
29
+ ],
30
+ stability: [
31
+ huskyHooks,
32
+ ciPipeline,
33
+ npmAudit
34
+ ],
35
+ codeQuality: [
36
+ apiResponseFormat
37
+ ],
38
+ supabase: [
39
+ rlsPolicyAudit
40
+ ]
41
+ }
42
+
43
+ /**
44
+ * Run all checks for a project
45
+ */
46
+ export async function runAllChecks(options = {}) {
47
+ const projectRoot = options.projectRoot || process.cwd()
48
+ const config = loadConfig(projectRoot)
49
+ const suites = options.suites || ['security', 'stability', 'codeQuality', 'supabase']
50
+
51
+ // Auto-detect Supabase
52
+ if (config.suites.supabase === 'auto') {
53
+ config.suites.supabase = detectSupabase(projectRoot)
54
+ }
55
+
56
+ const results = {
57
+ projectRoot,
58
+ timestamp: new Date().toISOString(),
59
+ passed: true,
60
+ suites: {},
61
+ summary: {
62
+ total: 0,
63
+ passed: 0,
64
+ failed: 0,
65
+ skipped: 0,
66
+ findings: { critical: 0, high: 0, medium: 0, low: 0 }
67
+ }
68
+ }
69
+
70
+ for (const suite of suites) {
71
+ if (!config.suites[suite]) {
72
+ continue
73
+ }
74
+
75
+ const checks = ALL_CHECKS[suite] || []
76
+ results.suites[suite] = {
77
+ passed: true,
78
+ checks: []
79
+ }
80
+
81
+ for (const check of checks) {
82
+ const checkResult = {
83
+ id: check.meta.id,
84
+ name: check.meta.name,
85
+ severity: check.meta.severity,
86
+ ...await check.run(config, projectRoot)
87
+ }
88
+
89
+ results.suites[suite].checks.push(checkResult)
90
+
91
+ // Update suite status
92
+ if (!checkResult.passed && !checkResult.skipped) {
93
+ results.suites[suite].passed = false
94
+ results.passed = false
95
+ results.summary.failed++
96
+ } else if (checkResult.skipped) {
97
+ results.summary.skipped++
98
+ } else {
99
+ results.summary.passed++
100
+ }
101
+
102
+ // Aggregate findings
103
+ results.summary.total++
104
+ if (checkResult.summary) {
105
+ results.summary.findings.critical += checkResult.summary.critical || 0
106
+ results.summary.findings.high += checkResult.summary.high || 0
107
+ results.summary.findings.medium += checkResult.summary.medium || 0
108
+ results.summary.findings.low += checkResult.summary.low || 0
109
+ }
110
+ }
111
+ }
112
+
113
+ return results
114
+ }
115
+
116
+ /**
117
+ * Run only security checks
118
+ */
119
+ export async function runSecurityChecks(options = {}) {
120
+ return runAllChecks({ ...options, suites: ['security'] })
121
+ }
122
+
123
+ /**
124
+ * Run only stability checks
125
+ */
126
+ export async function runStabilityChecks(options = {}) {
127
+ return runAllChecks({ ...options, suites: ['stability'] })
128
+ }
129
+
130
+ /**
131
+ * Run only code quality checks
132
+ */
133
+ export async function runCodeQualityChecks(options = {}) {
134
+ return runAllChecks({ ...options, suites: ['codeQuality'] })
135
+ }
136
+
137
+ /**
138
+ * Quick check - only critical checks
139
+ */
140
+ export async function runQuickCheck(options = {}) {
141
+ const projectRoot = options.projectRoot || process.cwd()
142
+ const config = loadConfig(projectRoot)
143
+
144
+ // Run only critical security checks
145
+ const criticalChecks = [
146
+ hardcodedSecrets,
147
+ serviceKeyExposure
148
+ ]
149
+
150
+ const results = {
151
+ projectRoot,
152
+ timestamp: new Date().toISOString(),
153
+ passed: true,
154
+ checks: [],
155
+ summary: { total: 0, passed: 0, failed: 0, findings: { critical: 0 } }
156
+ }
157
+
158
+ for (const check of criticalChecks) {
159
+ const checkResult = {
160
+ id: check.meta.id,
161
+ name: check.meta.name,
162
+ ...await check.run(config, projectRoot)
163
+ }
164
+
165
+ results.checks.push(checkResult)
166
+
167
+ if (!checkResult.passed) {
168
+ results.passed = false
169
+ results.summary.failed++
170
+ } else {
171
+ results.summary.passed++
172
+ }
173
+
174
+ results.summary.total++
175
+ results.summary.findings.critical += checkResult.summary?.critical || 0
176
+ }
177
+
178
+ return results
179
+ }
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@soulbatical/tetra-dev-toolkit",
3
+ "version": "1.1.0",
4
+ "publishConfig": {
5
+ "access": "restricted"
6
+ },
7
+ "description": "Developer toolkit for all VCA projects - audit, dev-token, quality checks",
8
+ "author": "Albert Barth <albertbarth@gmail.com>",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/mralbertzwolle/vca-quality-toolkit"
13
+ },
14
+ "keywords": [
15
+ "quality",
16
+ "security",
17
+ "audit",
18
+ "dev-token",
19
+ "eslint",
20
+ "supabase",
21
+ "rls",
22
+ "pre-commit",
23
+ "ci-cd"
24
+ ],
25
+ "type": "module",
26
+ "main": "lib/index.js",
27
+ "bin": {
28
+ "vca-audit": "./bin/vca-audit.js",
29
+ "vca-security": "./bin/vca-security.js",
30
+ "vca-stability": "./bin/vca-stability.js",
31
+ "vca-setup": "./bin/vca-setup.js",
32
+ "vca-dev-token": "./bin/vca-dev-token.js"
33
+ },
34
+ "files": [
35
+ "bin/",
36
+ "lib/",
37
+ "templates/"
38
+ ],
39
+ "scripts": {
40
+ "test": "node --test src/**/*.test.js",
41
+ "lint": "eslint src/ lib/ bin/",
42
+ "build": "echo 'No build step needed'",
43
+ "prepublishOnly": "npm test && npm run lint"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ },
48
+ "dependencies": {
49
+ "@supabase/supabase-js": "^2.48.0",
50
+ "chalk": "^5.3.0",
51
+ "commander": "^12.0.0",
52
+ "dotenv": "^16.4.7",
53
+ "glob": "^10.3.10",
54
+ "ora": "^8.0.1",
55
+ "yaml": "^2.8.2"
56
+ },
57
+ "devDependencies": {
58
+ "eslint": "^9.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "eslint": ">=8.0.0",
62
+ "typescript": ">=5.0.0"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "eslint": {
66
+ "optional": true
67
+ },
68
+ "typescript": {
69
+ "optional": true
70
+ }
71
+ }
72
+ }