@soulbatical/tetra-dev-toolkit 1.9.1 → 1.9.3

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,114 @@
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: build/deploy has tetra-check-rls --errors-only
72
+ // Check railway.json, Dockerfile, docker-compose, and CI workflows
73
+ const buildFiles = [
74
+ join(projectPath, 'railway.json'),
75
+ join(projectPath, '..', 'railway.json'),
76
+ join(projectPath, 'backend', 'railway.json'),
77
+ join(projectPath, 'Dockerfile'),
78
+ join(projectPath, 'backend', 'Dockerfile'),
79
+ join(projectPath, '.github', 'workflows', 'quality.yml'),
80
+ join(projectPath, '.github', 'workflows', 'deploy.yml'),
81
+ join(projectPath, '.github', 'workflows', 'ci.yml')
82
+ ]
83
+
84
+ for (const p of buildFiles) {
85
+ if (existsSync(p)) {
86
+ try {
87
+ const content = readFileSync(p, 'utf-8')
88
+ if (content.includes('tetra-check-rls')) {
89
+ result.details.layer3_build = true
90
+ result.details.layer3_source = p.split('/').slice(-2).join('/')
91
+ result.score += 1
92
+ break
93
+ }
94
+ } catch { /* ignore read errors */ }
95
+ }
96
+ }
97
+
98
+ if (!result.details.layer3_build) {
99
+ result.details.missing.push('Layer 3: No build/deploy file contains tetra-check-rls --errors-only (checked railway.json, Dockerfile, CI workflows)')
100
+ }
101
+
102
+ // Set status
103
+ if (result.score === 3) {
104
+ result.status = 'ok'
105
+ } else if (result.score >= 2) {
106
+ result.status = 'warning'
107
+ } else {
108
+ result.status = 'error'
109
+ }
110
+
111
+ result.details.fix = 'Run: npx tetra-setup hooks'
112
+
113
+ return result
114
+ }
@@ -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.3",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },