@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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-dev-toolkit",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },