cerber-core 1.1.2 → 1.1.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.
@@ -20,12 +20,14 @@ export const BACKEND_SCHEMA = {
20
20
  forbiddenPatterns: [
21
21
  // Uncomment and customize based on your tech stack:
22
22
  // {
23
- // pattern: /password\s*=\s*['"][^'"]+['"]/i,
23
+ // pattern: "password\\s*=\\s*['\"][^'\"]+['\"]",
24
+ // flags: "i",
24
25
  // name: "Hardcoded passwords",
25
26
  // severity: "error"
26
27
  // },
27
28
  // {
28
- // pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/i,
29
+ // pattern: "api[_-]?key\\s*=\\s*['\"][^'\"]+['\"]",
30
+ // flags: "i",
29
31
  // name: "Hardcoded API keys",
30
32
  // severity: "error"
31
33
  // }
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { execSync } from 'child_process';
6
6
  import fs from 'fs';
7
+ import { join } from 'path';
7
8
 
8
9
  const SCHEMA_FILE = '{{SCHEMA_FILE}}';
9
10
  const APPROVALS_TAG = '{{APPROVALS_TAG}}';
@@ -13,10 +14,23 @@ async function main() {
13
14
 
14
15
  if (!fs.existsSync(SCHEMA_FILE)) {
15
16
  console.error(`❌ Schema file not found: ${SCHEMA_FILE}`);
16
- console.error('Guardian MVP: schema missing. Add your rules to proceed.');
17
+ console.error('Create your schema file to enable validation.');
18
+ console.error(`Example: npx cerber init --print-schema-template > ${SCHEMA_FILE}`);
17
19
  process.exit(1);
18
20
  }
19
21
 
22
+ // Import schema
23
+ let schema;
24
+ try {
25
+ const schemaPath = join(process.cwd(), SCHEMA_FILE);
26
+ const schemaModule = await import(`file://${schemaPath}`);
27
+ schema = schemaModule.BACKEND_SCHEMA || schemaModule.default || schemaModule;
28
+ } catch (err) {
29
+ console.error(`❌ Failed to load schema from ${SCHEMA_FILE}:`, err.message);
30
+ process.exit(1);
31
+ }
32
+
33
+ // Get staged files
20
34
  let stagedFiles;
21
35
  try {
22
36
  stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
@@ -34,7 +48,97 @@ async function main() {
34
48
  }
35
49
 
36
50
  console.log(`Checking ${stagedFiles.length} file(s)...`);
37
- console.log('Guardian MVP: schema detected. Add validation rules to enforce imports/forbidden patterns.');
51
+
52
+ const violations = [];
53
+
54
+ // Validate each staged file
55
+ for (const file of stagedFiles) {
56
+ if (!fs.existsSync(file)) continue;
57
+
58
+ const content = fs.readFileSync(file, 'utf-8');
59
+
60
+ // Check forbidden patterns
61
+ if (schema.forbiddenPatterns && Array.isArray(schema.forbiddenPatterns)) {
62
+ for (const rule of schema.forbiddenPatterns) {
63
+ if (!rule.pattern) continue;
64
+
65
+ const pattern = typeof rule.pattern === 'string'
66
+ ? new RegExp(rule.pattern, rule.flags || 'i')
67
+ : rule.pattern;
68
+
69
+ // Check if file is in exceptions
70
+ if (rule.exceptions && rule.exceptions.some(ex => file.includes(ex))) {
71
+ continue;
72
+ }
73
+
74
+ if (pattern.test(content)) {
75
+ // Check for architect approval
76
+ const hasApproval = content.includes(APPROVALS_TAG);
77
+
78
+ if (!hasApproval) {
79
+ violations.push({
80
+ file,
81
+ rule: rule.name || 'Unnamed rule',
82
+ severity: rule.severity || 'error'
83
+ });
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ // Check required imports (rules)
90
+ if (schema.rules && Array.isArray(schema.rules)) {
91
+ for (const rule of schema.rules) {
92
+ if (!rule.pattern || !rule.requiredImports) continue;
93
+
94
+ const filePattern = typeof rule.pattern === 'string' ? new RegExp(rule.pattern) : rule.pattern;
95
+
96
+ if (!filePattern.test(file)) continue;
97
+
98
+ // Check if file is in exceptions
99
+ if (rule.exceptions && rule.exceptions.some(ex => file.includes(ex))) {
100
+ continue;
101
+ }
102
+
103
+ // Check each required import
104
+ for (const requiredImport of rule.requiredImports) {
105
+ const importPattern = new RegExp(`import.*${requiredImport}`, 'i');
106
+
107
+ if (!importPattern.test(content)) {
108
+ // Check for architect approval
109
+ const hasApproval = content.includes(APPROVALS_TAG);
110
+
111
+ if (!hasApproval) {
112
+ violations.push({
113
+ file,
114
+ rule: `${rule.name || 'Unnamed rule'}: missing import '${requiredImport}'`,
115
+ severity: rule.severity || 'error'
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ // Report violations
125
+ if (violations.length > 0) {
126
+ console.error('\n❌ Architecture violations detected:\n');
127
+
128
+ for (const v of violations) {
129
+ const icon = v.severity === 'error' ? '🔴' : '⚠️';
130
+ console.error(`${icon} [${v.severity.toUpperCase()}] ${v.file}`);
131
+ console.error(` ${v.rule}`);
132
+ console.error(` Add ${APPROVALS_TAG} comment to override\n`);
133
+ }
134
+
135
+ const errorCount = violations.filter(v => v.severity === 'error').length;
136
+ if (errorCount > 0) {
137
+ console.error(`\n❌ Commit blocked: ${errorCount} error(s) found`);
138
+ process.exit(1);
139
+ }
140
+ }
141
+
38
142
  console.log('✅ All checks passed');
39
143
  }
40
144
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cerber-core",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Your architecture roadmap becomes executable law. Guardian blocks commits that violate your plan. Pay architect once, Cerber enforces forever. Save $6k/month/dev.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,12 +20,14 @@ export const BACKEND_SCHEMA = {
20
20
  forbiddenPatterns: [
21
21
  // Uncomment and customize based on your tech stack:
22
22
  // {
23
- // pattern: /password\s*=\s*['"][^'"]+['"]/i,
23
+ // pattern: "password\\s*=\\s*['\"][^'\"]+['\"]",
24
+ // flags: "i",
24
25
  // name: "Hardcoded passwords",
25
26
  // severity: "error"
26
27
  // },
27
28
  // {
28
- // pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/i,
29
+ // pattern: "api[_-]?key\\s*=\\s*['\"][^'\"]+['\"]",
30
+ // flags: "i",
29
31
  // name: "Hardcoded API keys",
30
32
  // severity: "error"
31
33
  // }
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { execSync } from 'child_process';
6
6
  import fs from 'fs';
7
+ import { join } from 'path';
7
8
 
8
9
  const SCHEMA_FILE = '{{SCHEMA_FILE}}';
9
10
  const APPROVALS_TAG = '{{APPROVALS_TAG}}';
@@ -13,10 +14,23 @@ async function main() {
13
14
 
14
15
  if (!fs.existsSync(SCHEMA_FILE)) {
15
16
  console.error(`❌ Schema file not found: ${SCHEMA_FILE}`);
16
- console.error('Guardian MVP: schema missing. Add your rules to proceed.');
17
+ console.error('Create your schema file to enable validation.');
18
+ console.error(`Example: npx cerber init --print-schema-template > ${SCHEMA_FILE}`);
17
19
  process.exit(1);
18
20
  }
19
21
 
22
+ // Import schema
23
+ let schema;
24
+ try {
25
+ const schemaPath = join(process.cwd(), SCHEMA_FILE);
26
+ const schemaModule = await import(`file://${schemaPath}`);
27
+ schema = schemaModule.BACKEND_SCHEMA || schemaModule.default || schemaModule;
28
+ } catch (err) {
29
+ console.error(`❌ Failed to load schema from ${SCHEMA_FILE}:`, err.message);
30
+ process.exit(1);
31
+ }
32
+
33
+ // Get staged files
20
34
  let stagedFiles;
21
35
  try {
22
36
  stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
@@ -34,7 +48,97 @@ async function main() {
34
48
  }
35
49
 
36
50
  console.log(`Checking ${stagedFiles.length} file(s)...`);
37
- console.log('Guardian MVP: schema detected. Add validation rules to enforce imports/forbidden patterns.');
51
+
52
+ const violations = [];
53
+
54
+ // Validate each staged file
55
+ for (const file of stagedFiles) {
56
+ if (!fs.existsSync(file)) continue;
57
+
58
+ const content = fs.readFileSync(file, 'utf-8');
59
+
60
+ // Check forbidden patterns
61
+ if (schema.forbiddenPatterns && Array.isArray(schema.forbiddenPatterns)) {
62
+ for (const rule of schema.forbiddenPatterns) {
63
+ if (!rule.pattern) continue;
64
+
65
+ const pattern = typeof rule.pattern === 'string'
66
+ ? new RegExp(rule.pattern, rule.flags || 'i')
67
+ : rule.pattern;
68
+
69
+ // Check if file is in exceptions
70
+ if (rule.exceptions && rule.exceptions.some(ex => file.includes(ex))) {
71
+ continue;
72
+ }
73
+
74
+ if (pattern.test(content)) {
75
+ // Check for architect approval
76
+ const hasApproval = content.includes(APPROVALS_TAG);
77
+
78
+ if (!hasApproval) {
79
+ violations.push({
80
+ file,
81
+ rule: rule.name || 'Unnamed rule',
82
+ severity: rule.severity || 'error'
83
+ });
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ // Check required imports (rules)
90
+ if (schema.rules && Array.isArray(schema.rules)) {
91
+ for (const rule of schema.rules) {
92
+ if (!rule.pattern || !rule.requiredImports) continue;
93
+
94
+ const filePattern = typeof rule.pattern === 'string' ? new RegExp(rule.pattern) : rule.pattern;
95
+
96
+ if (!filePattern.test(file)) continue;
97
+
98
+ // Check if file is in exceptions
99
+ if (rule.exceptions && rule.exceptions.some(ex => file.includes(ex))) {
100
+ continue;
101
+ }
102
+
103
+ // Check each required import
104
+ for (const requiredImport of rule.requiredImports) {
105
+ const importPattern = new RegExp(`import.*${requiredImport}`, 'i');
106
+
107
+ if (!importPattern.test(content)) {
108
+ // Check for architect approval
109
+ const hasApproval = content.includes(APPROVALS_TAG);
110
+
111
+ if (!hasApproval) {
112
+ violations.push({
113
+ file,
114
+ rule: `${rule.name || 'Unnamed rule'}: missing import '${requiredImport}'`,
115
+ severity: rule.severity || 'error'
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ // Report violations
125
+ if (violations.length > 0) {
126
+ console.error('\n❌ Architecture violations detected:\n');
127
+
128
+ for (const v of violations) {
129
+ const icon = v.severity === 'error' ? '🔴' : '⚠️';
130
+ console.error(`${icon} [${v.severity.toUpperCase()}] ${v.file}`);
131
+ console.error(` ${v.rule}`);
132
+ console.error(` Add ${APPROVALS_TAG} comment to override\n`);
133
+ }
134
+
135
+ const errorCount = violations.filter(v => v.severity === 'error').length;
136
+ if (errorCount > 0) {
137
+ console.error(`\n❌ Commit blocked: ${errorCount} error(s) found`);
138
+ process.exit(1);
139
+ }
140
+ }
141
+
38
142
  console.log('✅ All checks passed');
39
143
  }
40
144
 
@@ -20,12 +20,14 @@ export const BACKEND_SCHEMA = {
20
20
  forbiddenPatterns: [
21
21
  // Uncomment and customize based on your tech stack:
22
22
  // {
23
- // pattern: /password\s*=\s*['"][^'"]+['"]/i,
23
+ // pattern: "password\\s*=\\s*['\"][^'\"]+['\"]",
24
+ // flags: "i",
24
25
  // name: "Hardcoded passwords",
25
26
  // severity: "error"
26
27
  // },
27
28
  // {
28
- // pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/i,
29
+ // pattern: "api[_-]?key\\s*=\\s*['\"][^'\"]+['\"]",
30
+ // flags: "i",
29
31
  // name: "Hardcoded API keys",
30
32
  // severity: "error"
31
33
  // }
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { execSync } from 'child_process';
6
6
  import fs from 'fs';
7
+ import { join } from 'path';
7
8
 
8
9
  const SCHEMA_FILE = '{{SCHEMA_FILE}}';
9
10
  const APPROVALS_TAG = '{{APPROVALS_TAG}}';
@@ -13,10 +14,23 @@ async function main() {
13
14
 
14
15
  if (!fs.existsSync(SCHEMA_FILE)) {
15
16
  console.error(`❌ Schema file not found: ${SCHEMA_FILE}`);
16
- console.error('Guardian MVP: schema missing. Add your rules to proceed.');
17
+ console.error('Create your schema file to enable validation.');
18
+ console.error(`Example: npx cerber init --print-schema-template > ${SCHEMA_FILE}`);
17
19
  process.exit(1);
18
20
  }
19
21
 
22
+ // Import schema
23
+ let schema;
24
+ try {
25
+ const schemaPath = join(process.cwd(), SCHEMA_FILE);
26
+ const schemaModule = await import(`file://${schemaPath}`);
27
+ schema = schemaModule.BACKEND_SCHEMA || schemaModule.default || schemaModule;
28
+ } catch (err) {
29
+ console.error(`❌ Failed to load schema from ${SCHEMA_FILE}:`, err.message);
30
+ process.exit(1);
31
+ }
32
+
33
+ // Get staged files
20
34
  let stagedFiles;
21
35
  try {
22
36
  stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
@@ -34,7 +48,97 @@ async function main() {
34
48
  }
35
49
 
36
50
  console.log(`Checking ${stagedFiles.length} file(s)...`);
37
- console.log('Guardian MVP: schema detected. Add validation rules to enforce imports/forbidden patterns.');
51
+
52
+ const violations = [];
53
+
54
+ // Validate each staged file
55
+ for (const file of stagedFiles) {
56
+ if (!fs.existsSync(file)) continue;
57
+
58
+ const content = fs.readFileSync(file, 'utf-8');
59
+
60
+ // Check forbidden patterns
61
+ if (schema.forbiddenPatterns && Array.isArray(schema.forbiddenPatterns)) {
62
+ for (const rule of schema.forbiddenPatterns) {
63
+ if (!rule.pattern) continue;
64
+
65
+ const pattern = typeof rule.pattern === 'string'
66
+ ? new RegExp(rule.pattern, rule.flags || 'i')
67
+ : rule.pattern;
68
+
69
+ // Check if file is in exceptions
70
+ if (rule.exceptions && rule.exceptions.some(ex => file.includes(ex))) {
71
+ continue;
72
+ }
73
+
74
+ if (pattern.test(content)) {
75
+ // Check for architect approval
76
+ const hasApproval = content.includes(APPROVALS_TAG);
77
+
78
+ if (!hasApproval) {
79
+ violations.push({
80
+ file,
81
+ rule: rule.name || 'Unnamed rule',
82
+ severity: rule.severity || 'error'
83
+ });
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ // Check required imports (rules)
90
+ if (schema.rules && Array.isArray(schema.rules)) {
91
+ for (const rule of schema.rules) {
92
+ if (!rule.pattern || !rule.requiredImports) continue;
93
+
94
+ const filePattern = typeof rule.pattern === 'string' ? new RegExp(rule.pattern) : rule.pattern;
95
+
96
+ if (!filePattern.test(file)) continue;
97
+
98
+ // Check if file is in exceptions
99
+ if (rule.exceptions && rule.exceptions.some(ex => file.includes(ex))) {
100
+ continue;
101
+ }
102
+
103
+ // Check each required import
104
+ for (const requiredImport of rule.requiredImports) {
105
+ const importPattern = new RegExp(`import.*${requiredImport}`, 'i');
106
+
107
+ if (!importPattern.test(content)) {
108
+ // Check for architect approval
109
+ const hasApproval = content.includes(APPROVALS_TAG);
110
+
111
+ if (!hasApproval) {
112
+ violations.push({
113
+ file,
114
+ rule: `${rule.name || 'Unnamed rule'}: missing import '${requiredImport}'`,
115
+ severity: rule.severity || 'error'
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ // Report violations
125
+ if (violations.length > 0) {
126
+ console.error('\n❌ Architecture violations detected:\n');
127
+
128
+ for (const v of violations) {
129
+ const icon = v.severity === 'error' ? '🔴' : '⚠️';
130
+ console.error(`${icon} [${v.severity.toUpperCase()}] ${v.file}`);
131
+ console.error(` ${v.rule}`);
132
+ console.error(` Add ${APPROVALS_TAG} comment to override\n`);
133
+ }
134
+
135
+ const errorCount = violations.filter(v => v.severity === 'error').length;
136
+ if (errorCount > 0) {
137
+ console.error(`\n❌ Commit blocked: ${errorCount} error(s) found`);
138
+ process.exit(1);
139
+ }
140
+ }
141
+
38
142
  console.log('✅ All checks passed');
39
143
  }
40
144