cem_acpt 0.11.2 → 0.12.0

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.
@@ -0,0 +1,120 @@
1
+ # CEM-6760 — Fix invalid CIS-CAT Pro Assessor flags in on-node scan daemon
2
+
3
+ ## Summary
4
+
5
+ The on-node scan daemon's `run_ciscat` helper in
6
+ `lib/terraform/gcp/linux/scan/scan_service.rb` passes two flags that
7
+ CIS-CAT Pro v4's `Assessor-CLI.sh` does not accept: `-rn ciscat-results`
8
+ and `-rjson`. Both are silently ignored — the assessor falls back to a
9
+ hostname-prefixed default filename instead of `ciscat-results.*`, and
10
+ no JSON report is ever written. Only the canonical ARF.xml lands in
11
+ the reports directory, after which the daemon 500s when its parser
12
+ tries to read the JSON file at the expected path. Swap the flags to
13
+ their real names (`-rp`, `-json`) and add `-nts` to suppress the
14
+ auto-appended timestamp so the file lands at the literal path the
15
+ parser already reads.
16
+
17
+ ## Functional Behavior
18
+
19
+ ### `lib/terraform/gcp/linux/scan/scan_service.rb#run_ciscat`
20
+
21
+ The method currently constructs the assessor command as
22
+ (`scan_service.rb:73-79`):
23
+
24
+ ```ruby
25
+ cmd = [
26
+ 'sudo', '/opt/cis-cat-pro/Assessor-CLI.sh',
27
+ '-rd', REPORT_DIR,
28
+ '-rn', 'ciscat-results',
29
+ '-rjson',
30
+ '-p', profile,
31
+ ]
32
+ ```
33
+
34
+ Replace `-rn` with `-rp` (the real `--report-prefix` flag), replace
35
+ `-rjson` with `-json` (the real JSON-output flag), and append `-nts`
36
+ (`--no-timestamp`) so the assessor does not insert a timestamp segment
37
+ in the filename:
38
+
39
+ ```ruby
40
+ cmd = [
41
+ 'sudo', '/opt/cis-cat-pro/Assessor-CLI.sh',
42
+ '-rd', REPORT_DIR,
43
+ '-rp', 'ciscat-results',
44
+ '-nts',
45
+ '-json',
46
+ '-p', profile,
47
+ ]
48
+ ```
49
+
50
+ With these three changes the assessor writes JSON to
51
+ `/opt/cem_acpt/scan/reports/ciscat-results.json` — the literal path
52
+ `json_out` at `scan_service.rb:72` already references and
53
+ `parse_ciscat_json` already reads.
54
+
55
+ The `-l <level>` conditional at `scan_service.rb:81` is unchanged.
56
+
57
+ ## Input/Output Contracts
58
+
59
+ No method signature changes. The command array passed to
60
+ `Open3.capture3` changes; everything downstream — `parse_ciscat_json`,
61
+ the `error` handling at `scan_service.rb:84`, and the `/scan` HTTP
62
+ response shape — sees the same data flow.
63
+
64
+ ## Constraints / Invariants
65
+
66
+ - `-rp`, `-json`, and `-nts` are all valid in CIS-CAT Pro v4.x
67
+ (confirmed against `Assessor-CLI.sh -h` on a v4.63.0-provisioned
68
+ node). The shipping `cem_acpt_scan` flow installs the assessor from
69
+ the operator-supplied bundle, so any v4.x release works.
70
+ - ARF.xml continues to be emitted as a side effect — we do not pass
71
+ `-narf`. Keeping ARF makes it easier to diagnose future scan
72
+ failures by hand on the node.
73
+ - The `json_out` path constant at `scan_service.rb:72` does not change.
74
+
75
+ ## Tests
76
+
77
+ Add a text-grep regression spec at
78
+ `spec/terraform/gcp/linux/scan/scan_service_spec.rb` (new path,
79
+ mirroring `lib/`). The spec reads `scan_service.rb` as a string and
80
+ asserts the corrected flag tokens are present and the old ones absent:
81
+
82
+ - `'-rp', 'ciscat-results'` present, `'-rn', 'ciscat-results'` absent.
83
+ - `'-json'` present, `'-rjson'` absent.
84
+ - `'-nts'` present.
85
+
86
+ No execution, no `Open3` stubbing, no eval-slicing of the script. The
87
+ behavioral validation (the assessor actually accepting these flags)
88
+ was performed by hand on a v4.63.0 node; the spec's only job is to
89
+ catch an accidental revert.
90
+
91
+ ## Non-Goals
92
+
93
+ - **Stderr capture on scan failure.** Tracked by CEM-6761.
94
+ - **Missing `-b <benchmark>` flag.** Tracked by CEM-6759. Without it
95
+ the assessor still exits without scanning anything; this spec only
96
+ addresses flag-name correctness, not benchmark identification.
97
+ - **JSON key-name mismatch in `parse_ciscat_json`.** Tracked by
98
+ CEM-6762.
99
+ - **Refactoring `scan_service.rb` for broader testability beyond what
100
+ is needed to assert the new flag set.**
101
+
102
+ ## Acceptance Criteria
103
+
104
+ - [ ] `run_ciscat` passes `-rp ciscat-results` instead of
105
+ `-rn ciscat-results`.
106
+ - [ ] `run_ciscat` passes `-json` instead of `-rjson`.
107
+ - [ ] `run_ciscat` passes `-nts` so the output filename has no
108
+ timestamp segment.
109
+ - [ ] RSpec regression coverage at
110
+ `spec/terraform/gcp/linux/scan/scan_service_spec.rb` asserts the
111
+ corrected flag tokens are present and the old ones absent.
112
+ - [ ] `bundle exec rake spec` passes.
113
+
114
+ ## Files Touched
115
+
116
+ - `lib/terraform/gcp/linux/scan/scan_service.rb` — `run_ciscat` flag
117
+ fix.
118
+ - `spec/terraform/gcp/linux/scan/scan_service_spec.rb` — new
119
+ text-grep regression spec.
120
+ - `specifications/CEM-6760.md` — this file.
@@ -0,0 +1,136 @@
1
+ # CEM-6761 — Surface assessor stderr when result file is missing
2
+
3
+ ## Summary
4
+
5
+ `run_ciscat` and `run_openscap` in `lib/terraform/gcp/linux/scan/scan_service.rb` use
6
+ `||=` to attach the assessor/oscap exit-code message to `parsed['error']`. Because
7
+ `parse_ciscat_json` / `parse_openscap_xml` set `parsed['error']` to a "Result file
8
+ missing" string whenever the output file is absent, `||=` short-circuits and the
9
+ actual assessor stderr — the reason the file was never written — is silently discarded.
10
+ Engineers debugging a failed scan see only `"Result file missing: …"` with no indication
11
+ of what the scanner printed, making root-cause analysis guesswork.
12
+
13
+ ## Functional Behavior
14
+
15
+ ### `run_ciscat` (`scan_service.rb:71–86`)
16
+
17
+ **Before:**
18
+
19
+ ```ruby
20
+ _stdout, stderr, status = Open3.capture3(*cmd)
21
+ parsed = parse_ciscat_json(json_out)
22
+ parsed['error'] ||= "Assessor-CLI exited #{status.exitstatus}: #{stderr}" unless status.success? || File.exist?(json_out)
23
+ parsed
24
+ ```
25
+
26
+ **After:**
27
+
28
+ ```ruby
29
+ _stdout, stderr, status = Open3.capture3(*cmd)
30
+ parsed = parse_ciscat_json(json_out)
31
+ unless status.success? || File.exist?(json_out)
32
+ assessor_msg = "Assessor-CLI exited #{status.exitstatus}: #{stderr.strip}"
33
+ parsed['error'] = parsed['error'] ? "#{assessor_msg}; #{parsed['error']}" : assessor_msg
34
+ end
35
+ parsed
36
+ ```
37
+
38
+ ### `run_openscap` (`scan_service.rb:31–45`)
39
+
40
+ **Before:**
41
+
42
+ ```ruby
43
+ _stdout, stderr, status = Open3.capture3(*cmd)
44
+ parsed = parse_openscap_xml(xml_out)
45
+ parsed['error'] ||= "oscap exited #{status.exitstatus}: #{stderr}" unless status.success? || File.exist?(xml_out)
46
+ parsed
47
+ ```
48
+
49
+ **After:**
50
+
51
+ ```ruby
52
+ _stdout, stderr, status = Open3.capture3(*cmd)
53
+ parsed = parse_openscap_xml(xml_out)
54
+ unless status.success? || File.exist?(xml_out)
55
+ oscap_msg = "oscap exited #{status.exitstatus}: #{stderr.strip}"
56
+ parsed['error'] = parsed['error'] ? "#{oscap_msg}; #{parsed['error']}" : oscap_msg
57
+ end
58
+ parsed
59
+ ```
60
+
61
+ Key changes:
62
+ - `||=` replaced with explicit conditional assignment so both messages are preserved.
63
+ - `stderr.strip` trims trailing newlines from the assessor output before embedding it.
64
+ - Assessor/oscap message is prepended, separated by `"; "`, so the tool-level failure
65
+ is the first thing the reader sees.
66
+
67
+ ## Input/Output Contracts
68
+
69
+ **Failure path input:** `Open3.capture3` returns a non-zero exit status *and* the
70
+ result file was not written (the normal failure mode when the assessor crashes early).
71
+
72
+ **Output:** The `'error'` key in the returned hash contains both messages joined by
73
+ `"; "`:
74
+
75
+ ```
76
+ "Assessor-CLI exited 1: <stderr text>; Result file missing: /opt/cem_acpt/scan/reports/ciscat-results.json"
77
+ ```
78
+
79
+ This string flows back through `perform_scan` → `/scan` endpoint → HTTP 500 response
80
+ body, where the host-side `DaemonClient` surfaces it in the test failure output.
81
+
82
+ ## Edge Cases
83
+
84
+ - **Assessor exits non-zero but file was written anyway** — the `|| File.exist?` guard
85
+ is unchanged; stderr is not attached in this case. The parse result stands on its own.
86
+ - **`stderr` is empty or only whitespace** — `.strip` collapses it to `""`, so the
87
+ message reads `"Assessor-CLI exited 1: "` — still correct, not a crash.
88
+ - **`parse_ciscat_json` returns no `'error'` key** — `parsed['error']` is `nil`;
89
+ the ternary falls through to the bare `assessor_msg` assignment. Equivalent to
90
+ the original `||=` behaviour.
91
+
92
+ ## Constraints / Invariants
93
+
94
+ - The guard condition `unless status.success? || File.exist?(json_out)` is unchanged.
95
+ This condition defines when stderr is surfaced and must not be relaxed.
96
+ - The normalized output hash shape remains compatible with `CemAcpt::Scan::Result`.
97
+ No callers change.
98
+
99
+ ## Non-Goals
100
+
101
+ - Adding a `-b <benchmark>` flag to `run_ciscat` (CEM-6759).
102
+ - Fixing the CIS-CAT Pro JSON key names in `parse_ciscat_json` (CEM-6762 — already done).
103
+ - Capturing assessor stdout; only stderr is relevant for error reporting.
104
+
105
+ ## Tests
106
+
107
+ Extend `spec/terraform/gcp/linux/scan/scan_service_spec.rb` with two new `describe`
108
+ blocks — one for `run_ciscat` and one for `run_openscap` — that stub `Open3.capture3`
109
+ and exercise the combined-error-message path. The script is already loaded via the
110
+ `SCAN_SERVICE_LOADED` guard, so `run_ciscat` and `run_openscap` are available directly.
111
+
112
+ For `run_ciscat`: stub `Open3.capture3` to return `['', 'Permission denied', double(exitstatus: 1, success?: false)]`
113
+ and pass a nonexistent `json_out` path (the stub never writes a file). Assert that the
114
+ returned `'error'` string contains both `"Assessor-CLI exited 1"` and `"Result file missing"`.
115
+
116
+ For `run_openscap`: same pattern with `oscap` stderr. Assert that `'error'` contains
117
+ both `"oscap exited 1"` and `"Result file missing"`.
118
+
119
+ ## Acceptance Criteria
120
+
121
+ - [ ] `run_ciscat` uses explicit conditional assignment instead of `||=`.
122
+ - [ ] `run_openscap` uses explicit conditional assignment instead of `||=`.
123
+ - [ ] When the result file is absent and the assessor exits non-zero, the `'error'`
124
+ value includes both the exit-code/stderr message and the "Result file missing" message.
125
+ - [ ] `.strip` is applied to `stderr` before embedding.
126
+ - [ ] All existing specs pass.
127
+ - [ ] New unit tests for the combined-error path pass for both `run_ciscat` and
128
+ `run_openscap`.
129
+
130
+ ## Files Touched
131
+
132
+ - `lib/terraform/gcp/linux/scan/scan_service.rb` — fix `run_ciscat` and `run_openscap`.
133
+ - `spec/fixtures/config_testing/user_config_dir/terraform/gcp/linux/scan/scan_service.rb` — mirror update.
134
+ - `spec/fixtures/config_testing/user_config_dir/terraform_checksum.txt` — checksum update.
135
+ - `spec/terraform/gcp/linux/scan/scan_service_spec.rb` — new unit-test contexts for
136
+ both methods.
@@ -0,0 +1,163 @@
1
+ # CEM-6762 — Parse CIS-CAT Pro v4 rules JSON keys correctly
2
+
3
+ ## Summary
4
+
5
+ `parse_ciscat_json` in `lib/terraform/gcp/linux/scan/scan_service.rb` reads the wrong
6
+ top-level array key and wrong per-entry keys out of the CIS-CAT Pro Assessor v4 JSON
7
+ report. The parser expects `raw['results']` at the top level, but v4 emits `raw['rules']`.
8
+ Per-entry, it tries `r['id'] || r['rule_id']` for the rule identifier and `r['severity']`
9
+ for severity; v4 uses `r['rule-id']` (hyphen, not underscore) and emits no `severity` field.
10
+ The result-value count bucketing maps `'notapplicable'` to `not_applicable_count`, but v4
11
+ emits `'notchecked'` and `'informational'` instead.
12
+
13
+ Because the key names don't match, `raw['rules']` resolves to nil, the parser falls back to
14
+ an empty array, and every CIS-CAT Pro scan reports zero passes and zero fails regardless of
15
+ what the assessor found. The top-level `score` key is correct and `.to_f` handles the string
16
+ `"85.95"` format v4 uses, so the score is not broken — this masks the breakage in smoke
17
+ tests. Confirmed against actual `Assessor-CLI.sh -json` output on RHEL 8 while validating
18
+ CEM-6511.
19
+
20
+ ## Functional Behavior
21
+
22
+ ### `parse_ciscat_json` (`scan_service.rb:88–109`)
23
+
24
+ **Before:**
25
+
26
+ ```ruby
27
+ results = (raw['results'] || []).map do |r|
28
+ {
29
+ 'id' => r['id'] || r['rule_id'],
30
+ 'severity' => r['severity'],
31
+ 'result' => r['result'] || r['status'],
32
+ }
33
+ end
34
+ score = (raw['score'] || raw['compliance_score'] || 0.0).to_f
35
+ counts = results.group_by { |r| (r['result'] || '').to_s.downcase }.transform_values(&:size)
36
+ {
37
+ 'score' => score,
38
+ 'passed_count' => counts['pass'] || 0,
39
+ 'failed_count' => counts['fail'] || 0,
40
+ 'not_applicable_count' => counts['notapplicable'] || 0,
41
+ 'error_count' => counts['error'] || 0,
42
+ 'rules' => results,
43
+ }
44
+ ```
45
+
46
+ **After:**
47
+
48
+ ```ruby
49
+ results = (raw['rules'] || []).map do |r|
50
+ {
51
+ 'id' => r['rule-id'],
52
+ 'result' => r['result'],
53
+ }
54
+ end
55
+ score = (raw['score'] || 0.0).to_f
56
+ counts = results.group_by { |r| (r['result'] || '').to_s.downcase }.transform_values(&:size)
57
+ {
58
+ 'score' => score,
59
+ 'passed_count' => counts['pass'] || 0,
60
+ 'failed_count' => counts['fail'] || 0,
61
+ 'not_applicable_count' => (counts['notchecked'] || 0) + (counts['notapplicable'] || 0),
62
+ 'error_count' => (counts['error'] || 0) + (counts['informational'] || 0),
63
+ 'rules' => results,
64
+ }
65
+ ```
66
+
67
+ Key changes:
68
+ - `raw['results']` → `raw['rules']` (v4 top-level key).
69
+ - `r['id'] || r['rule_id']` → `r['rule-id']` (v4 per-entry key; hyphen, not underscore).
70
+ - `r['result'] || r['status']` → `r['result']` (v4 only uses `result`).
71
+ - `severity` field dropped; v4 does not emit it.
72
+ - `not_applicable_count` sums `notchecked` + `notapplicable` — v4 uses `notchecked`; the
73
+ `notapplicable` term is kept as a defensive fallback for format variants.
74
+ - `error_count` sums `error` + `informational` — v4 uses `informational` for rules that are
75
+ advisory only.
76
+ - `raw['compliance_score']` fallback removed; not present in v4.
77
+
78
+ ## Input/Output Contracts
79
+
80
+ **Input:** Path to a CIS-CAT Pro v4 JSON report file. Confirmed v4 shape:
81
+
82
+ ```json
83
+ {
84
+ "benchmark-id": "xccdf_org.cisecurity.benchmarks_benchmark_4.0.0_CIS_...",
85
+ "benchmark-title": "CIS Red Hat Enterprise Linux 8 Benchmark",
86
+ "profile-id": "xccdf_org.cisecurity.benchmarks_profile_Level_2_-_Server",
87
+ "score": "85.95",
88
+ "rules": [
89
+ { "rule-id": "xccdf_org.cisecurity.benchmarks_rule_1.1.1.1_...", "rule-title": "...", "result": "pass" },
90
+ { "rule-id": "xccdf_org.cisecurity.benchmarks_rule_6.1.2_...", "rule-title": "...", "result": "fail" },
91
+ { "rule-id": "xccdf_org.cisecurity.benchmarks_rule_6.2.1.1.2_...","rule-title": "...", "result": "notchecked" },
92
+ { "rule-id": "xccdf_org.cisecurity.benchmarks_rule_5.4.1.2_...", "rule-title": "...", "result": "informational" }
93
+ ]
94
+ }
95
+ ```
96
+
97
+ **Output:** Hash compatible with the host-side `Scan::Result` constructor (shape unchanged):
98
+
99
+ ```ruby
100
+ {
101
+ 'score' => 85.95, # Float; score string coerced with .to_f
102
+ 'passed_count' => Integer,
103
+ 'failed_count' => Integer,
104
+ 'not_applicable_count' => Integer, # notchecked + notapplicable
105
+ 'error_count' => Integer, # error + informational
106
+ 'rules' => [{ 'id' => String, 'result' => String }, ...],
107
+ }
108
+ ```
109
+
110
+ ## Edge Cases
111
+
112
+ - **File missing:** returns `{ 'error' => "Result file missing: #{path}" }` — unchanged.
113
+ - **Empty `rules` array:** all counts zero, `rules` is `[]`; no crash.
114
+ - **`score` as string:** `.to_f` on `"85.95"` returns `85.95`; handled correctly.
115
+ - **`notapplicable` in future format variants:** included in `not_applicable_count` sum as a defensive fallback.
116
+
117
+ ## Constraints / Invariants
118
+
119
+ - Normalized output hash shape must remain compatible with `CemAcpt::Scan::Result#initialize`. No callers change.
120
+ - `rule-title` is not included in normalized rule entries; `rule-id` is sufficient as the unique identifier.
121
+
122
+ ## Non-Goals
123
+
124
+ - Adding the `-b <benchmark>` flag to `run_ciscat` (CEM-6759).
125
+ - Surfacing assessor stderr on failure (CEM-6761).
126
+ - Parsing OpenSCAP XML results (`parse_openscap_xml` is a separate method and is unaffected).
127
+
128
+ ## Tests
129
+
130
+ Extend `spec/terraform/gcp/linux/scan/scan_service_spec.rb` with a new `describe` context
131
+ for `parse_ciscat_json`. Because `scan_service.rb` is a standalone script that starts a
132
+ WEBrick server at load time, the spec loads it via `load` after stubbing
133
+ `WEBrick::HTTPServer.new` (returning a double that silently absorbs `mount_proc`, `start`,
134
+ and `shutdown`) and `FileUtils.mkdir_p` (to avoid creating `/opt/cem_acpt/scan/reports` on
135
+ the test host). After loading, `parse_ciscat_json` is defined at the top level and callable
136
+ directly.
137
+
138
+ The fixture is a small inline JSON string mirroring the confirmed v4 shape, written to a
139
+ `Tempfile`. Tests assert:
140
+
141
+ - All four result values (`pass`, `fail`, `notchecked`, `informational`) are counted in the
142
+ correct bucket.
143
+ - `rules` entries carry `id` from `rule-id` and `result` from `result`; no `severity` key.
144
+ - A non-existent path returns `{ 'error' => /Result file missing/ }`.
145
+
146
+ The existing CEM-6760 text-grep assertions are untouched.
147
+
148
+ ## Acceptance Criteria
149
+
150
+ - [ ] `parse_ciscat_json` reads `raw['rules']` instead of `raw['results']`.
151
+ - [ ] Rule entries use `r['rule-id']` for `id`; `r['rule_id']` and `r['id']` are no longer referenced.
152
+ - [ ] `severity` is no longer extracted from rule entries.
153
+ - [ ] `not_applicable_count` counts `notchecked` and `notapplicable` result values.
154
+ - [ ] `error_count` counts `error` and `informational` result values.
155
+ - [ ] All existing specs pass.
156
+ - [ ] New unit tests for `parse_ciscat_json` pass with a v4-shaped fixture.
157
+
158
+ ## Files Touched
159
+
160
+ - `lib/terraform/gcp/linux/scan/scan_service.rb` — fix `parse_ciscat_json`.
161
+ - `spec/fixtures/config_testing/user_config_dir/terraform/gcp/linux/scan/scan_service.rb` — mirror update.
162
+ - `spec/fixtures/config_testing/user_config_dir/terraform_checksum.txt` — checksum update.
163
+ - `spec/terraform/gcp/linux/scan/scan_service_spec.rb` — new `parse_ciscat_json` unit-test context.
@@ -0,0 +1,101 @@
1
+ # CEM-6765 — Remove stray `-l` flag from CIS-CAT Pro Assessor invocation
2
+
3
+ ## Summary
4
+
5
+ The on-node scan daemon's `run_ciscat` helper in `lib/terraform/gcp/linux/scan/scan_service.rb` appends `-l <level>` to the assessor command. In CIS-CAT Pro v4, `-l` is `--list` ("List available assessment content"), not a level argument. The assessor parses `-l 2`, ignores the `2`, treats the invocation as a list request, prints the numbered benchmark catalog, exits zero, and writes no JSON report. This silently hijacks every CIS-CAT Pro scan regardless of how the rest of the pipeline is configured — masking CEM-6759's `-b` wiring and every prior blocker fix. Confirmed against `Assessor-CLI.sh -h` on a v4.63.0-provisioned node: `-l` has no level meaning. Scan level is encoded in the profile id (`Level_2_-_Server`), not a separate flag.
6
+
7
+ Drop the `-l` flag, drop the now-unused `level` parameter from `run_ciscat`, and drop the corresponding argument from the `perform_scan` call site.
8
+
9
+ ## Functional Behavior
10
+
11
+ ### `lib/terraform/gcp/linux/scan/scan_service.rb#run_ciscat`
12
+
13
+ The method currently constructs the assessor command as (`scan_service.rb:74-85`):
14
+
15
+ ```ruby
16
+ def run_ciscat(profile, level, benchmark)
17
+ json_out = File.join(REPORT_DIR, 'ciscat-results.json')
18
+ cmd = [
19
+ 'sudo', '/opt/cis-cat-pro/Assessor-CLI.sh',
20
+ '-rd', REPORT_DIR,
21
+ '-rp', 'ciscat-results',
22
+ '-nts',
23
+ '-json',
24
+ '-p', profile,
25
+ ]
26
+ cmd += ['-b', "/opt/cis-cat-pro/benchmarks/#{benchmark}"] if benchmark
27
+ cmd += ['-l', level.to_s] if level
28
+ ```
29
+
30
+ Drop the `level` parameter and the `-l` append:
31
+
32
+ ```ruby
33
+ def run_ciscat(profile, benchmark)
34
+ json_out = File.join(REPORT_DIR, 'ciscat-results.json')
35
+ cmd = [
36
+ 'sudo', '/opt/cis-cat-pro/Assessor-CLI.sh',
37
+ '-rd', REPORT_DIR,
38
+ '-rp', 'ciscat-results',
39
+ '-nts',
40
+ '-json',
41
+ '-p', profile,
42
+ ]
43
+ cmd += ['-b', "/opt/cis-cat-pro/benchmarks/#{benchmark}"] if benchmark
44
+ ```
45
+
46
+ ### `lib/terraform/gcp/linux/scan/scan_service.rb#perform_scan`
47
+
48
+ Update the call site (`scan_service.rb:124`):
49
+
50
+ ```ruby
51
+ # before:
52
+ when 'ciscat'
53
+ run_ciscat(cfg['profile'], cfg['level'], cfg['benchmark'])
54
+
55
+ # after:
56
+ when 'ciscat'
57
+ run_ciscat(cfg['profile'], cfg['benchmark'])
58
+ ```
59
+
60
+ The `cfg['level']` key remains in the daemon's `scan_config.json` payload — it is unused by `run_ciscat` after this change but harmless. Removing it from the host-side JSON payload is left as a follow-up cleanup, not a blocker.
61
+
62
+ ## Input/Output Contracts
63
+
64
+ `run_ciscat` loses its `level` positional argument; signature shrinks from three positional args to two. Everything downstream (`parse_ciscat_json`, the error handling at `scan_service.rb:87-90`, and the `/scan` HTTP response shape) is unchanged.
65
+
66
+ ## Constraints / Invariants
67
+
68
+ - `-l` is `--list` in CIS-CAT Pro v4.x and has no level meaning — confirmed from `Assessor-CLI.sh -h` on a v4.63.0 node during CEM-6765's investigation. There is no separate level flag in the v4 CLI.
69
+ - Scan level is already encoded in the profile id (e.g. `xccdf_org.cisecurity.benchmarks_profile_Level_2_-_Server`). Removing the redundant `-l` does not change which controls the assessor evaluates.
70
+ - `test_data[:scan][:level]` is unaffected. It is still extracted by `test_data.rb`'s `vars_post_processing` from the test-case name pattern and remains available in the scan config payload for logging and openscap.
71
+
72
+ ## Tests
73
+
74
+ Add a text-grep regression to `spec/terraform/gcp/linux/scan/scan_service_spec.rb` asserting `'-l'` is absent from `run_ciscat`'s command array.
75
+
76
+ Update the existing CEM-6761 `run_ciscat` stub tests at `spec/terraform/gcp/linux/scan/scan_service_spec.rb:171` to drop the level positional argument — `run_ciscat('profile-id', 'benchmark.xml')` instead of `run_ciscat('profile-id', 2, 'benchmark.xml')`.
77
+
78
+ Behavioral validation (the assessor producing a JSON report with the corrected command) was performed by hand on a v4.63.0 node during CEM-6765's investigation. That evidence is stronger than any unit test we could write without a live assessor binary.
79
+
80
+ ## Non-Goals
81
+
82
+ - **Removing `cfg['level']` from `scan_config.json`.** The level remains in the payload as a harmless metadata field. Removing it would touch `provision/terraform.rb` and ripple through CEM-6759's tests for no functional gain.
83
+ - **Dropping `level` extraction from `test_data.rb`.** It is still useful for logging and openscap.
84
+ - **Refactoring `run_ciscat` further.**
85
+
86
+ ## Acceptance Criteria
87
+
88
+ - [ ] `run_ciscat` no longer passes `-l` to `Assessor-CLI.sh`.
89
+ - [ ] `run_ciscat`'s signature is `(profile, benchmark)` — no `level` parameter.
90
+ - [ ] `perform_scan` calls `run_ciscat(cfg['profile'], cfg['benchmark'])`.
91
+ - [ ] RSpec regression coverage at `spec/terraform/gcp/linux/scan/scan_service_spec.rb` asserts `'-l'` is not in the script.
92
+ - [ ] CEM-6761 `run_ciscat` stub tests pass the new two-arg signature.
93
+ - [ ] `bundle exec rake spec` passes.
94
+
95
+ ## Files Touched
96
+
97
+ - `lib/terraform/gcp/linux/scan/scan_service.rb` — drop `-l` flag and `level` parameter.
98
+ - `spec/fixtures/config_testing/user_config_dir/terraform/gcp/linux/scan/scan_service.rb` — fixture mirror.
99
+ - `spec/fixtures/config_testing/user_config_dir/terraform_checksum.txt` — updated checksum.
100
+ - `spec/terraform/gcp/linux/scan/scan_service_spec.rb` — `-l` absence text-grep, two-arg `run_ciscat` calls.
101
+ - `specifications/CEM-6765.md` — this file.
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cem_acpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.2
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - puppetlabs
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-06-08 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: async-http
@@ -219,6 +220,7 @@ email:
219
220
  executables:
220
221
  - cem_acpt
221
222
  - cem_acpt_image
223
+ - cem_acpt_scan
222
224
  extensions: []
223
225
  extra_rdoc_files: []
224
226
  files:
@@ -252,6 +254,7 @@ files:
252
254
  - docs/rfcs/README.md
253
255
  - exe/cem_acpt
254
256
  - exe/cem_acpt_image
257
+ - exe/cem_acpt_scan
255
258
  - lib/cem_acpt.rb
256
259
  - lib/cem_acpt/actions.rb
257
260
  - lib/cem_acpt/bolt.rb
@@ -272,6 +275,7 @@ files:
272
275
  - lib/cem_acpt/config/base.rb
273
276
  - lib/cem_acpt/config/cem_acpt.rb
274
277
  - lib/cem_acpt/config/cem_acpt_image.rb
278
+ - lib/cem_acpt/config/cem_acpt_scan.rb
275
279
  - lib/cem_acpt/core_ext.rb
276
280
  - lib/cem_acpt/goss.rb
277
281
  - lib/cem_acpt/goss/api.rb
@@ -286,12 +290,15 @@ files:
286
290
  - lib/cem_acpt/platform.rb
287
291
  - lib/cem_acpt/platform/base.rb
288
292
  - lib/cem_acpt/platform/gcp.rb
289
- - lib/cem_acpt/provision.rb
290
293
  - lib/cem_acpt/provision/terraform.rb
291
294
  - lib/cem_acpt/provision/terraform/linux.rb
292
295
  - lib/cem_acpt/provision/terraform/os_data.rb
293
296
  - lib/cem_acpt/provision/terraform/terraform_cmd.rb
294
297
  - lib/cem_acpt/provision/terraform/windows.rb
298
+ - lib/cem_acpt/scan.rb
299
+ - lib/cem_acpt/scan/daemon_client.rb
300
+ - lib/cem_acpt/scan/errors.rb
301
+ - lib/cem_acpt/scan/result.rb
295
302
  - lib/cem_acpt/test_data.rb
296
303
  - lib/cem_acpt/test_runner.rb
297
304
  - lib/cem_acpt/test_runner/log_formatter.rb
@@ -299,6 +306,7 @@ files:
299
306
  - lib/cem_acpt/test_runner/log_formatter/bolt_summary_results_formatter.rb
300
307
  - lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb
301
308
  - lib/cem_acpt/test_runner/log_formatter/goss_error_formatter.rb
309
+ - lib/cem_acpt/test_runner/log_formatter/scan_result_formatter.rb
302
310
  - lib/cem_acpt/test_runner/log_formatter/standard_error_formatter.rb
303
311
  - lib/cem_acpt/test_runner/test_results.rb
304
312
  - lib/cem_acpt/utils.rb
@@ -314,6 +322,8 @@ files:
314
322
  - lib/terraform/gcp/linux/goss/puppet_noop.yaml
315
323
  - lib/terraform/gcp/linux/log_service/log_service.rb
316
324
  - lib/terraform/gcp/linux/main.tf
325
+ - lib/terraform/gcp/linux/scan/scan_service.rb
326
+ - lib/terraform/gcp/linux/scan/scan_service.service
317
327
  - lib/terraform/gcp/linux/systemd/goss-acpt.service
318
328
  - lib/terraform/gcp/linux/systemd/goss-idempotent.service
319
329
  - lib/terraform/gcp/linux/systemd/goss-noop.service
@@ -324,6 +334,7 @@ files:
324
334
  - lib/terraform/image/gcp/linux/main.tf
325
335
  - lib/terraform/image/gcp/windows/.keep
326
336
  - sample_config.yaml
337
+ - specifications/CEM-6511.md
327
338
  - specifications/CEM-6713.md
328
339
  - specifications/CEM-6714.md
329
340
  - specifications/CEM-6715.md
@@ -331,6 +342,12 @@ files:
331
342
  - specifications/CEM-6717.md
332
343
  - specifications/CEM-6718.md
333
344
  - specifications/CEM-6719.md
345
+ - specifications/CEM-6720.md
346
+ - specifications/CEM-6759.md
347
+ - specifications/CEM-6760.md
348
+ - specifications/CEM-6761.md
349
+ - specifications/CEM-6762.md
350
+ - specifications/CEM-6765.md
334
351
  homepage: https://github.com/puppetlabs/cem_acpt
335
352
  licenses:
336
353
  - proprietary
@@ -338,6 +355,7 @@ metadata:
338
355
  homepage_uri: https://github.com/puppetlabs/cem_acpt
339
356
  source_code_uri: https://github.com/puppetlabs/cem_acpt
340
357
  changelog_uri: https://github.com/puppetlabs/cem_acpt
358
+ post_install_message:
341
359
  rdoc_options: []
342
360
  require_paths:
343
361
  - lib
@@ -352,7 +370,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
352
370
  - !ruby/object:Gem::Version
353
371
  version: '0'
354
372
  requirements: []
355
- rubygems_version: 3.6.9
373
+ rubygems_version: 3.5.22
374
+ signing_key:
356
375
  specification_version: 4
357
376
  summary: CEM Acceptance Tests
358
377
  test_files: []
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'logging'
4
- require_relative 'provision/terraform'
5
-
6
- module CemAcpt
7
- module Provision
8
- include CemAcpt::Logging
9
-
10
- def self.new_provisioner(config, provision_data)
11
- case config.get('provisioner')
12
- when 'terraform'
13
- logger.debug('CemAcpt::Provision') { 'Using Terraform provisioner' }
14
- CemAcpt::Provision::Terraform.new(config, provision_data)
15
- else
16
- raise ArgumentError, "Unknown provisioner #{config.get('provisioner')}"
17
- end
18
- end
19
- end
20
- end