sec-gate 0.1.1 → 0.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sec-gate",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Pre-commit security gate for OWASP Top 10 2021 — SAST, SCA and misconfig checks for Node/Express, Go and React codebases",
5
5
  "author": {
6
6
  "name": "Sundram Bhardwaj",
package/src/cli.js CHANGED
@@ -2,7 +2,16 @@ const path = require('path');
2
2
 
3
3
  function usage() {
4
4
  // eslint-disable-next-line no-console
5
- console.log(`sec-gate - OWASP Top 10 security gate\n\nUsage:\n sec-gate install\n sec-gate scan --staged\n\nCommands:\n install Installs the local git pre-commit hook for this repo\n scan Runs SAST/SCA checks (supports --staged)\n`);
5
+ console.log([
6
+ 'sec-gate - OWASP Top 10 security gate',
7
+ '',
8
+ 'Usage:',
9
+ ' sec-gate install Install the pre-commit hook in this repo',
10
+ ' sec-gate scan Scan all tracked files',
11
+ ' sec-gate scan --staged Scan only staged files (used by pre-commit hook)',
12
+ ' sec-gate doctor Check all components are installed and working',
13
+ ''
14
+ ].join('\n'));
6
15
  }
7
16
 
8
17
  function parseArgs(argv) {
@@ -38,6 +47,12 @@ async function run() {
38
47
  return;
39
48
  }
40
49
 
50
+ if (cmd === 'doctor') {
51
+ const { doctor } = require('./commands/doctor');
52
+ await doctor();
53
+ return;
54
+ }
55
+
41
56
  usage();
42
57
  process.exit(1);
43
58
  }
@@ -0,0 +1,103 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execFileSync } = require('child_process');
4
+
5
+ function check(label, fn) {
6
+ try {
7
+ const result = fn();
8
+ console.log(` [OK] ${label}${result ? ': ' + result : ''}`);
9
+ return true;
10
+ } catch (err) {
11
+ console.log(` [FAIL] ${label}: ${err.message}`);
12
+ return false;
13
+ }
14
+ }
15
+
16
+ async function doctor() {
17
+ console.log('\nsec-gate doctor — checking all components\n');
18
+
19
+ // 1. sec-gate itself
20
+ check('sec-gate CLI', () => {
21
+ const pkg = require('../../package.json');
22
+ return `v${pkg.version}`;
23
+ });
24
+
25
+ // 2. @pensar/semgrep-node
26
+ console.log('\n--- SAST (Semgrep) ---');
27
+ const semgrepOk = check('@pensar/semgrep-node installed', () => {
28
+ const pkg = require('@pensar/semgrep-node');
29
+ return 'found';
30
+ });
31
+
32
+ if (semgrepOk) {
33
+ // Try actually loading the binary by doing a tiny scan on a temp file
34
+ await (async () => {
35
+ try {
36
+ const os = require('os');
37
+ const tmpFile = path.join(os.tmpdir(), 'sec-gate-test.js');
38
+ fs.writeFileSync(tmpFile, '// test\nconst x = 1;\n');
39
+ const scan = require('@pensar/semgrep-node').default;
40
+ await scan(tmpFile, { language: 'js', ruleSets: ['owasp-top10'] });
41
+ fs.unlinkSync(tmpFile);
42
+ console.log(' [OK] semgrep binary: working');
43
+ } catch (err) {
44
+ console.log(` [FAIL] semgrep binary: ${err.message}`);
45
+ console.log(' Fix: the semgrep binary may not be downloaded yet.');
46
+ console.log(' Try running `sec-gate scan` on a JS file once to trigger download.');
47
+ }
48
+ })();
49
+ }
50
+
51
+ // 3. osv-scanner
52
+ console.log('\n--- SCA: Node/pnpm (OSV-Scanner) ---');
53
+ const ext = process.platform === 'win32' ? '.exe' : '';
54
+ const vendorOsv = path.join(__dirname, '..', '..', 'vendor-bin', `osv-scanner${ext}`);
55
+
56
+ check('osv-scanner binary (vendor-bin)', () => {
57
+ if (fs.existsSync(vendorOsv)) return vendorOsv;
58
+ throw new Error('not found in vendor-bin');
59
+ });
60
+
61
+ check('osv-scanner executable', () => {
62
+ if (!fs.existsSync(vendorOsv)) throw new Error('binary missing');
63
+ const out = execFileSync(vendorOsv, ['--version'], { encoding: 'utf8' }).trim();
64
+ return out;
65
+ });
66
+
67
+ // 4. govulncheck
68
+ console.log('\n--- SCA: Go (govulncheck) ---');
69
+ const vendorGo = path.join(__dirname, '..', '..', 'vendor-bin', `govulncheck${ext}`);
70
+
71
+ check('govulncheck binary (vendor-bin)', () => {
72
+ if (fs.existsSync(vendorGo)) return vendorGo;
73
+ throw new Error('not found — Go SCA will be skipped (Go may not be installed)');
74
+ });
75
+
76
+ // 5. git hook
77
+ console.log('\n--- Pre-commit hook ---');
78
+ check('git available', () => {
79
+ execFileSync('git', ['--version'], { stdio: ['ignore', 'pipe', 'ignore'] });
80
+ return 'found';
81
+ });
82
+
83
+ try {
84
+ const repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
85
+ encoding: 'utf8',
86
+ stdio: ['ignore', 'pipe', 'ignore']
87
+ }).trim();
88
+
89
+ const hookPath = path.join(repoRoot, '.git', 'hooks', 'pre-commit');
90
+ check('pre-commit hook installed', () => {
91
+ if (!fs.existsSync(hookPath)) throw new Error('not found — run `sec-gate install`');
92
+ const content = fs.readFileSync(hookPath, 'utf8');
93
+ if (!content.includes('installed-by: sec-gate')) throw new Error('hook exists but was not installed by sec-gate');
94
+ return hookPath;
95
+ });
96
+ } catch {
97
+ console.log(' [SKIP] pre-commit hook: not inside a git repo');
98
+ }
99
+
100
+ console.log('\nsec-gate doctor done.\n');
101
+ }
102
+
103
+ module.exports = { doctor };
@@ -34,24 +34,37 @@ async function runSemgrep({ files }) {
34
34
 
35
35
  for (const filePath of files) {
36
36
  const lang = getLanguage(filePath);
37
+ // security-scan: disable rule-id: path-join-resolve-traversal reason: filePath comes from `git diff --cached --name-only` output, not from user input
37
38
  const absPath = path.resolve(filePath);
38
39
 
39
40
  try {
40
- // Scan with OWASP Top 10 rules bundled inside @pensar/semgrep-node
41
+ // eslint-disable-next-line no-console
42
+ console.log(`sec-gate: scanning ${filePath} (${lang}) with owasp-top10 rules...`);
43
+
41
44
  const issues = await semgrepScan(absPath, {
42
45
  language: lang,
43
46
  ruleSets: ['owasp-top10']
44
47
  });
45
48
 
49
+ if (issues.length > 0) {
50
+ // eslint-disable-next-line no-console
51
+ console.log(`sec-gate: found ${issues.length} finding(s) in ${filePath}`);
52
+ }
53
+
46
54
  for (const issue of issues) {
47
55
  allFindings.push(normalizeSemgrepNodeFinding(issue, filePath));
48
56
  }
49
57
  } catch (err) {
50
- // If semgrep binary not yet downloaded for this platform, warn but don't crash.
51
58
  if (err && err.message && err.message.includes('ENOENT')) {
52
- console.warn(`sec-gate: semgrep binary not ready for ${lang}; skipping ${filePath}`);
59
+ // eslint-disable-next-line no-console
60
+ console.warn(`sec-gate: WARNING — semgrep binary not found for language "${lang}"`);
61
+ // eslint-disable-next-line no-console
62
+ console.warn(' The semgrep binary needs to be downloaded by @pensar/semgrep-node.');
63
+ // eslint-disable-next-line no-console
64
+ console.warn(' Run `sec-gate doctor` to diagnose, or re-install: npm i -g sec-gate');
53
65
  } else {
54
- throw err;
66
+ // eslint-disable-next-line no-console
67
+ console.warn(`sec-gate: WARNING — semgrep scan failed for ${filePath}: ${err.message}`);
55
68
  }
56
69
  }
57
70
  }