configsentry 0.0.24 → 0.0.26
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/README.md +21 -5
- package/dist/cli.js +42 -4
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npx configsentry ./docker-compose.yml
|
|
|
15
15
|
### GitHub Action (minimal)
|
|
16
16
|
|
|
17
17
|
```yml
|
|
18
|
-
- uses: alfredMorgenstern/configsentry@v0.0.
|
|
18
|
+
- uses: alfredMorgenstern/configsentry@v0.0.25
|
|
19
19
|
with:
|
|
20
20
|
target: .
|
|
21
21
|
```
|
|
@@ -27,7 +27,7 @@ permissions:
|
|
|
27
27
|
contents: read
|
|
28
28
|
security-events: write
|
|
29
29
|
|
|
30
|
-
- uses: alfredMorgenstern/configsentry@v0.0.
|
|
30
|
+
- uses: alfredMorgenstern/configsentry@v0.0.25
|
|
31
31
|
with:
|
|
32
32
|
target: .
|
|
33
33
|
sarif: true
|
|
@@ -72,10 +72,16 @@ node dist/cli.js --target ./docker-compose.yml
|
|
|
72
72
|
node dist/cli.js --target ./docker-compose.yml --format json
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
Write JSON to a file (no shell redirection needed):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
node dist/cli.js --target ./docker-compose.yml --format json --output configsentry.json
|
|
79
|
+
```
|
|
80
|
+
|
|
75
81
|
### SARIF output (GitHub Code Scanning)
|
|
76
82
|
|
|
77
83
|
```bash
|
|
78
|
-
node dist/cli.js --target ./docker-compose.yml --format sarif
|
|
84
|
+
node dist/cli.js --target ./docker-compose.yml --format sarif --output configsentry.sarif.json
|
|
79
85
|
```
|
|
80
86
|
|
|
81
87
|
## Baselines (incremental adoption)
|
|
@@ -146,7 +152,7 @@ jobs:
|
|
|
146
152
|
runs-on: ubuntu-latest
|
|
147
153
|
steps:
|
|
148
154
|
- uses: actions/checkout@v4
|
|
149
|
-
- uses: alfredMorgenstern/configsentry@v0.0.
|
|
155
|
+
- uses: alfredMorgenstern/configsentry@v0.0.25
|
|
150
156
|
with:
|
|
151
157
|
target: .
|
|
152
158
|
# optional: baseline: .configsentry-baseline.json
|
|
@@ -160,13 +166,23 @@ jobs:
|
|
|
160
166
|
|
|
161
167
|
**Note (consumer repos):** your repo does **not** need a `package-lock.json`. The action installs/builds ConfigSentry from the action package itself.
|
|
162
168
|
|
|
163
|
-
> Tip: pin to a tag (like `v0.0.
|
|
169
|
+
> Tip: pin to a tag (like `v0.0.25`) for reproducible builds.
|
|
164
170
|
|
|
165
171
|
## Exit codes
|
|
166
172
|
- `0` no findings
|
|
167
173
|
- `2` findings present
|
|
168
174
|
- `1` error
|
|
169
175
|
|
|
176
|
+
## CI: fail only on high severity (optional)
|
|
177
|
+
|
|
178
|
+
If you want ConfigSentry to block builds only on high severity findings:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npx configsentry ./docker-compose.yml --severity-threshold high
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
This also works in GitHub Actions via `args:` (see `docs/action-usage.md`).
|
|
185
|
+
|
|
170
186
|
## Example
|
|
171
187
|
|
|
172
188
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,9 @@ import { runRules } from './rules.js';
|
|
|
8
8
|
import { findingsToSarif } from './sarif.js';
|
|
9
9
|
import { resolveTargets } from './scan.js';
|
|
10
10
|
import { applyBaseline, loadBaseline, writeBaseline } from './baseline.js';
|
|
11
|
+
function severityRank(s) {
|
|
12
|
+
return s === 'low' ? 1 : s === 'medium' ? 2 : 3;
|
|
13
|
+
}
|
|
11
14
|
function parseArgs(argv) {
|
|
12
15
|
const args = argv.slice(2);
|
|
13
16
|
const help = args.includes('-h') || args.includes('--help');
|
|
@@ -29,13 +32,17 @@ function parseArgs(argv) {
|
|
|
29
32
|
const baselinePath = baselineIdx >= 0 ? args[baselineIdx + 1] : undefined;
|
|
30
33
|
const writeBaselineIdx = args.indexOf('--write-baseline');
|
|
31
34
|
const writeBaselinePath = writeBaselineIdx >= 0 ? args[writeBaselineIdx + 1] : undefined;
|
|
35
|
+
const severityThresholdIdx = args.indexOf('--severity-threshold');
|
|
36
|
+
const severityThreshold = severityThresholdIdx >= 0 ? args[severityThresholdIdx + 1] : undefined;
|
|
37
|
+
const outputIdx = args.indexOf('--output');
|
|
38
|
+
const outputPath = outputIdx >= 0 ? args[outputIdx + 1] : undefined;
|
|
32
39
|
// Prefer explicit flag (matches the GitHub Action input)
|
|
33
40
|
const targetIdx = args.indexOf('--target');
|
|
34
41
|
const targetFromFlag = targetIdx >= 0 ? args[targetIdx + 1] : undefined;
|
|
35
42
|
// Back-compat: first positional arg
|
|
36
43
|
const targetFromPositional = args.find((a) => !a.startsWith('-'));
|
|
37
44
|
const target = targetFromFlag ?? targetFromPositional;
|
|
38
|
-
return { args, help, version, output, format, baselinePath, writeBaselinePath, target };
|
|
45
|
+
return { args, help, version, output, format, outputPath, baselinePath, writeBaselinePath, severityThreshold, target };
|
|
39
46
|
}
|
|
40
47
|
function usage() {
|
|
41
48
|
console.log(`ConfigSentry (MVP)
|
|
@@ -48,6 +55,9 @@ Output:
|
|
|
48
55
|
--json machine-readable findings (deprecated; use --format json)
|
|
49
56
|
--sarif SARIF 2.1.0 (for GitHub code scanning) (deprecated; use --format sarif)
|
|
50
57
|
--format <pretty|json|sarif>
|
|
58
|
+
--output <file> write JSON/SARIF output to a file (use with --format)
|
|
59
|
+
--severity-threshold <low|medium|high>
|
|
60
|
+
only report findings at/above this severity (affects exit code)
|
|
51
61
|
|
|
52
62
|
Baselines:
|
|
53
63
|
--baseline <file> suppress findings present in a baseline file
|
|
@@ -60,7 +70,7 @@ Exit codes:
|
|
|
60
70
|
`);
|
|
61
71
|
}
|
|
62
72
|
async function main() {
|
|
63
|
-
const { args, help, version, output, format, baselinePath, writeBaselinePath, target } = parseArgs(process.argv);
|
|
73
|
+
const { args, help, version, output, format, outputPath, baselinePath, writeBaselinePath, severityThreshold, target } = parseArgs(process.argv);
|
|
64
74
|
if (version) {
|
|
65
75
|
try {
|
|
66
76
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -90,6 +100,14 @@ async function main() {
|
|
|
90
100
|
console.error('Error: choose only one output mode: --json, --sarif, or --format');
|
|
91
101
|
process.exit(1);
|
|
92
102
|
}
|
|
103
|
+
if (outputPath && output === 'pretty') {
|
|
104
|
+
console.error('Error: --output requires machine output (use --format json or --format sarif)');
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
if (severityThreshold && severityThreshold !== 'low' && severityThreshold !== 'medium' && severityThreshold !== 'high') {
|
|
108
|
+
console.error(`Error: invalid --severity-threshold '${severityThreshold}'. Expected: low | medium | high`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
93
111
|
if (!target) {
|
|
94
112
|
usage();
|
|
95
113
|
process.exit(1);
|
|
@@ -113,6 +131,14 @@ async function main() {
|
|
|
113
131
|
findings = res.kept;
|
|
114
132
|
suppressed = res.suppressed;
|
|
115
133
|
}
|
|
134
|
+
// Severity threshold filtering (affects reporting + exit code)
|
|
135
|
+
if (severityThreshold) {
|
|
136
|
+
const min = severityRank(severityThreshold);
|
|
137
|
+
findings = findings.filter((f) => {
|
|
138
|
+
const sev = f.severity ?? 'low';
|
|
139
|
+
return severityRank(sev) >= min;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
116
142
|
// Baseline generation mode
|
|
117
143
|
if (writeBaselinePath) {
|
|
118
144
|
await writeBaseline(path.resolve(writeBaselinePath), allFindings);
|
|
@@ -120,10 +146,22 @@ async function main() {
|
|
|
120
146
|
process.exit(0);
|
|
121
147
|
}
|
|
122
148
|
if (output === 'json') {
|
|
123
|
-
|
|
149
|
+
const payload = JSON.stringify({ targetPaths, findings, suppressedCount: suppressed.length }, null, 2);
|
|
150
|
+
if (outputPath) {
|
|
151
|
+
await fs.writeFile(path.resolve(outputPath), payload, 'utf8');
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
console.log(payload);
|
|
155
|
+
}
|
|
124
156
|
}
|
|
125
157
|
else if (output === 'sarif') {
|
|
126
|
-
|
|
158
|
+
const payload = JSON.stringify(findingsToSarif(findings), null, 2);
|
|
159
|
+
if (outputPath) {
|
|
160
|
+
await fs.writeFile(path.resolve(outputPath), payload, 'utf8');
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log(payload);
|
|
164
|
+
}
|
|
127
165
|
}
|
|
128
166
|
else {
|
|
129
167
|
const scope = targetPaths.length === 1 ? targetPaths[0] : `${targetPaths.length} file(s)`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "configsentry",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "Developer-first guardrails for docker-compose.yml (security + ops footguns).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
"prepack": "npm run build",
|
|
32
32
|
"start": "node dist/cli.js",
|
|
33
33
|
"dev": "node --loader ts-node/esm src/cli.ts",
|
|
34
|
-
"lint:example": "node dist/cli.js ./example.docker-compose.yml"
|
|
34
|
+
"lint:example": "node dist/cli.js ./example.docker-compose.yml",
|
|
35
|
+
"demo": "node dist/cli.js --target ./docs/demo/docker-compose.demo.yml",
|
|
36
|
+
"demo:json": "node dist/cli.js --target ./docs/demo/docker-compose.demo.yml --format json --output /tmp/configsentry-demo.json"
|
|
35
37
|
},
|
|
36
38
|
"keywords": [
|
|
37
39
|
"docker",
|