@soulbatical/tetra-dev-toolkit 1.9.1 → 1.9.2
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.
|
@@ -37,3 +37,4 @@ export { check as checkDependencyAutomation } from './dependency-automation.js'
|
|
|
37
37
|
export { check as checkLicenseAudit } from './license-audit.js'
|
|
38
38
|
export { check as checkSast } from './sast.js'
|
|
39
39
|
export { check as checkBundleSize } from './bundle-size.js'
|
|
40
|
+
export { check as checkSecurityLayers } from './security-layers.js'
|
|
@@ -33,6 +33,7 @@ import { check as checkDependencyAutomation } from './dependency-automation.js'
|
|
|
33
33
|
import { check as checkLicenseAudit } from './license-audit.js'
|
|
34
34
|
import { check as checkSast } from './sast.js'
|
|
35
35
|
import { check as checkBundleSize } from './bundle-size.js'
|
|
36
|
+
import { check as checkSecurityLayers } from './security-layers.js'
|
|
36
37
|
import { calculateHealthStatus } from './types.js'
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -74,7 +75,8 @@ export async function scanProjectHealth(projectPath, projectName, options = {})
|
|
|
74
75
|
checkDependencyAutomation(projectPath),
|
|
75
76
|
checkLicenseAudit(projectPath),
|
|
76
77
|
checkSast(projectPath),
|
|
77
|
-
checkBundleSize(projectPath)
|
|
78
|
+
checkBundleSize(projectPath),
|
|
79
|
+
checkSecurityLayers(projectPath)
|
|
78
80
|
])
|
|
79
81
|
|
|
80
82
|
const totalScore = checks.reduce((sum, c) => sum + c.score, 0)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check: 3-Layer Security Model
|
|
3
|
+
*
|
|
4
|
+
* Verifies the project has all 3 security layers active:
|
|
5
|
+
* Layer 1 (pre-commit): tetra-audit quick
|
|
6
|
+
* Layer 2 (pre-push): tetra-check-rls
|
|
7
|
+
* Layer 3 (build/deploy): tetra-check-rls --errors-only in railway.json
|
|
8
|
+
*
|
|
9
|
+
* Score: 0-3 (1 point per layer)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readFileSync } from 'fs'
|
|
13
|
+
import { join } from 'path'
|
|
14
|
+
import { createCheck } from './types.js'
|
|
15
|
+
|
|
16
|
+
export async function check(projectPath) {
|
|
17
|
+
const result = createCheck('security-layers', 3, {
|
|
18
|
+
layer1_precommit: false,
|
|
19
|
+
layer2_prepush: false,
|
|
20
|
+
layer3_build: false,
|
|
21
|
+
missing: []
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Layer 1: pre-commit has tetra-audit
|
|
25
|
+
const preCommitPaths = [
|
|
26
|
+
join(projectPath, '.husky', 'pre-commit'),
|
|
27
|
+
// monorepo: check parent dir too
|
|
28
|
+
join(projectPath, '..', '.husky', 'pre-commit')
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
for (const p of preCommitPaths) {
|
|
32
|
+
if (existsSync(p)) {
|
|
33
|
+
try {
|
|
34
|
+
const content = readFileSync(p, 'utf-8')
|
|
35
|
+
if (content.includes('tetra-audit')) {
|
|
36
|
+
result.details.layer1_precommit = true
|
|
37
|
+
result.score += 1
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
} catch { /* ignore read errors */ }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!result.details.layer1_precommit) {
|
|
45
|
+
result.details.missing.push('Layer 1: .husky/pre-commit missing tetra-audit quick')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Layer 2: pre-push has tetra-check-rls
|
|
49
|
+
const prePushPaths = [
|
|
50
|
+
join(projectPath, '.husky', 'pre-push'),
|
|
51
|
+
join(projectPath, '..', '.husky', 'pre-push')
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
for (const p of prePushPaths) {
|
|
55
|
+
if (existsSync(p)) {
|
|
56
|
+
try {
|
|
57
|
+
const content = readFileSync(p, 'utf-8')
|
|
58
|
+
if (content.includes('tetra-check-rls')) {
|
|
59
|
+
result.details.layer2_prepush = true
|
|
60
|
+
result.score += 1
|
|
61
|
+
break
|
|
62
|
+
}
|
|
63
|
+
} catch { /* ignore read errors */ }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!result.details.layer2_prepush) {
|
|
68
|
+
result.details.missing.push('Layer 2: .husky/pre-push missing tetra-check-rls')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Layer 3: railway.json has tetra-check-rls --errors-only
|
|
72
|
+
const railwayPaths = [
|
|
73
|
+
join(projectPath, 'railway.json'),
|
|
74
|
+
join(projectPath, '..', 'railway.json'),
|
|
75
|
+
join(projectPath, 'backend', 'railway.json')
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
for (const p of railwayPaths) {
|
|
79
|
+
if (existsSync(p)) {
|
|
80
|
+
try {
|
|
81
|
+
const content = readFileSync(p, 'utf-8')
|
|
82
|
+
if (content.includes('tetra-check-rls')) {
|
|
83
|
+
result.details.layer3_build = true
|
|
84
|
+
result.score += 1
|
|
85
|
+
break
|
|
86
|
+
}
|
|
87
|
+
} catch { /* ignore read errors */ }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!result.details.layer3_build) {
|
|
92
|
+
result.details.missing.push('Layer 3: railway.json missing tetra-check-rls --errors-only in buildCommand')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Set status
|
|
96
|
+
if (result.score === 3) {
|
|
97
|
+
result.status = 'ok'
|
|
98
|
+
} else if (result.score >= 2) {
|
|
99
|
+
result.status = 'warning'
|
|
100
|
+
} else {
|
|
101
|
+
result.status = 'error'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
result.details.fix = 'Run: npx tetra-setup hooks'
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @typedef {'plugins'|'mcps'|'git'|'tests'|'secrets'|'quality-toolkit'|'naming-conventions'|'rls-audit'|'rpc-param-mismatch'|'typescript-strict'|'prettier'|'coverage-thresholds'|'eslint-security'|'dependency-cruiser'|'conventional-commits'|'knip'|'dependency-automation'|'license-audit'|'sast'|'bundle-size'|'gitignore'|'repo-visibility'|'vincifox-widget'|'stella-integration'|'claude-md'|'doppler-compliance'|'infrastructure-yml'|'file-organization'} HealthCheckType
|
|
8
|
+
* @typedef {'plugins'|'mcps'|'git'|'tests'|'secrets'|'quality-toolkit'|'naming-conventions'|'rls-audit'|'rpc-param-mismatch'|'typescript-strict'|'prettier'|'coverage-thresholds'|'eslint-security'|'dependency-cruiser'|'conventional-commits'|'knip'|'dependency-automation'|'license-audit'|'sast'|'bundle-size'|'gitignore'|'repo-visibility'|'vincifox-widget'|'stella-integration'|'claude-md'|'doppler-compliance'|'infrastructure-yml'|'file-organization'|'security-layers'} HealthCheckType
|
|
9
9
|
*
|
|
10
10
|
* @typedef {'ok'|'warning'|'error'} HealthStatus
|
|
11
11
|
*
|
|
@@ -66,7 +66,7 @@ export function calculateHealthStatus(checks) {
|
|
|
66
66
|
|
|
67
67
|
// Critical checks override percentage
|
|
68
68
|
if (checks.some(c =>
|
|
69
|
-
(c.type === 'secrets' || c.type === 'rls-audit' || c.type === 'rpc-param-mismatch' || c.type === 'repo-visibility') && c.status === 'error'
|
|
69
|
+
(c.type === 'secrets' || c.type === 'rls-audit' || c.type === 'rpc-param-mismatch' || c.type === 'repo-visibility' || c.type === 'security-layers') && c.status === 'error'
|
|
70
70
|
)) {
|
|
71
71
|
return 'unhealthy'
|
|
72
72
|
}
|