moose-inventory 2.1 → 2.1.1
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/BACKLOG.md +15 -8
- data/Gemfile.lock +1 -1
- data/docs/release/publishing.md +1 -1
- data/docs/release/release-environment-protection.md +14 -6
- data/docs/security-audit-2026-05-29-snapshot-import-fuzz.md +58 -0
- data/lib/moose_inventory/cli/application.rb +2 -0
- data/lib/moose_inventory/operations/inventory_snapshot_validator.rb +59 -19
- data/lib/moose_inventory/version.rb +1 -1
- data/scripts/ci/check_security.sh +4 -1
- data/spec/lib/moose_inventory/cli/application_import_export_spec.rb +32 -0
- data/spec/lib/moose_inventory/operations/import_inventory_snapshot_spec.rb +13 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba11da77ca5c7033211f845076713ac540041e719f2a5a8bf4c75d84c98924db
|
|
4
|
+
data.tar.gz: 1c39004653d1aba885d38e966a91f7bea1c4bc9dc4579b98bdf57e2e333fecc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f565f1f646917e1cde181f5f068ffca699452327f4b1d72621cabc2b5997c2022c369b312e842ed6346e860d382f277e7dd7c234a182796c8e80cb70118e007
|
|
7
|
+
data.tar.gz: ec127be873c29e00feda3d2cde569d583b4d025c66b70e10c5b288f2d59c7f4b21b35bae851d143b244b513269cebae0108815d163efe0483c6aedb8f86488ad
|
data/.gitignore
CHANGED
data/BACKLOG.md
CHANGED
|
@@ -99,17 +99,20 @@ _No open process conformance items._
|
|
|
99
99
|
|
|
100
100
|
# Moose Inventory Architecture Follow-up Backlog
|
|
101
101
|
|
|
102
|
-
Architecture follow-up status counts:
|
|
102
|
+
Architecture follow-up status counts: 5 done / 0 open.
|
|
103
103
|
|
|
104
104
|
## Open
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
- GitHub accepted a custom deployment policy named `v*`, but the API reports the policy object as `type: branch`.
|
|
108
|
-
- On the next intentional `v*` tag release, verify that the release job can deploy to the `release` environment after required approval.
|
|
109
|
-
- If GitHub treats the policy as branch-only and blocks tag deployments, adjust the environment policy or document the limitation and rely on the workflow trigger plus tag/version check for tag control.
|
|
106
|
+
_No open architecture follow-up items._
|
|
110
107
|
|
|
111
108
|
## Done
|
|
112
109
|
|
|
110
|
+
1. Verify GitHub `release` environment custom `v*` policy behavior on the next real release.
|
|
111
|
+
- Verified during the intentional `v2.1` release.
|
|
112
|
+
- Initial release workflow run `26670139178` was rejected because GitHub treated the existing `v*` deployment policy as `type: branch`, which did not allow tag deployments.
|
|
113
|
+
- After explicit Russ approval, replaced the branch-typed policy with `v*` `type: tag`, reran and approved the release deployment, and published `moose-inventory` 2.1 successfully.
|
|
114
|
+
- Documented the verified tag-policy behavior in `docs/release/release-environment-protection.md` and `docs/release/publishing.md`.
|
|
115
|
+
|
|
113
116
|
1. Expand user database backup/restore guidance beyond SQLite.
|
|
114
117
|
- Added `docs/maintenance/database-backup-restore-guidance.md` documenting SQLite, MySQL/MariaDB, and PostgreSQL backup/restore boundaries.
|
|
115
118
|
- Clarified that Moose Inventory can inspect status, run migrations, run doctor checks, back up SQLite files, and export snapshots, but does not run server-backed dump/restore commands, manage grants/users, or implement destructive restore/sync semantics.
|
|
@@ -595,14 +598,18 @@ _No open modernization items._
|
|
|
595
598
|
|
|
596
599
|
# Moose Inventory Code Quality Backlog
|
|
597
600
|
|
|
598
|
-
Code quality status counts:
|
|
601
|
+
Code quality status counts: 67 done / 0 open.
|
|
599
602
|
|
|
600
603
|
## Open
|
|
601
604
|
|
|
602
|
-
_No open code-quality items._
|
|
603
|
-
|
|
604
605
|
## Done
|
|
605
606
|
|
|
607
|
+
1. Sanitize non-syntax Psych safe-load failures during snapshot import.
|
|
608
|
+
- Added a `Psych::Exception` rescue after the syntax-specific parse handler so aliases and disallowed classes are rejected with a single sanitized `ERROR:` line instead of Ruby/Psych stack traces.
|
|
609
|
+
- Added focused CLI regression coverage for alias and disallowed-class payloads during non-mutating `import --preview`.
|
|
610
|
+
- Verified with targeted snapshot-import fuzzing and `MOOSE_INVENTORY_REQUIRE_SECURITY_TOOLS=1 ./scripts/check.sh` on 2026-05-30.
|
|
611
|
+
- Evidence: `.openclaw-security-audit/fuzz_snapshot_import_availability_run_2026-05-30T0208Z-after-fix.json`.
|
|
612
|
+
|
|
606
613
|
1. Add focused specs for `OperationEventSupport` result defaults and event construction.
|
|
607
614
|
- Added direct unit coverage for default empty event payloads, explicit payload preservation, event emission, default `warning_count: 0`, and explicit warning counts.
|
|
608
615
|
- Added the new helper spec to the targeted RuboCop gate.
|
data/Gemfile.lock
CHANGED
data/docs/release/publishing.md
CHANGED
|
@@ -20,7 +20,7 @@ RubyGems has a trusted publisher configured for the existing `moose-inventory` g
|
|
|
20
20
|
|
|
21
21
|
The release workflow requires the GitHub environment name `release`. Current environment protection evidence is documented in `docs/release/release-environment-protection.md`.
|
|
22
22
|
|
|
23
|
-
As of 2026-05-29, the GitHub `release` environment has required reviewer protection for `RusDavies`, self-review prevention disabled, admin bypass disabled, and a custom deployment policy named `v
|
|
23
|
+
As of 2026-05-29, the GitHub `release` environment has required reviewer protection for `RusDavies`, self-review prevention disabled, admin bypass disabled, and a custom deployment policy named `v*` with `type: tag`. Self-review prevention is disabled because OpenClaw/automation pushes use Russ's GitHub account; with `RusDavies` as the only required reviewer, enabling self-review prevention could prevent Russ from approving the deployment. The workflow itself still runs only for pushed `v*` tags and verifies tag/version alignment before publishing. Tag deployment behavior was verified by the successful `v2.1` release workflow after replacing the initial branch-typed `v*` policy with a tag-typed policy.
|
|
24
24
|
|
|
25
25
|
Package provenance hardening beyond RubyGems trusted publishing is evaluated in `docs/release/package-provenance-hardening.md`. Additional checksums, GitHub artifact attestations, signatures, or SBOM publication are future hardening options, not current release blockers.
|
|
26
26
|
|
|
@@ -9,14 +9,18 @@ This document records the current GitHub `release` environment protection settin
|
|
|
9
9
|
Initial confirmation date: 2026-05-29
|
|
10
10
|
Protection configuration date: 2026-05-29
|
|
11
11
|
Self-review adjustment date: 2026-05-29
|
|
12
|
+
Tag policy correction/verification date: 2026-05-29
|
|
12
13
|
|
|
13
14
|
Confirmation/configuration commands:
|
|
14
15
|
|
|
15
16
|
```bash
|
|
16
17
|
gh api repos/RusDavies/moose-inventory/environments/release --jq '.'
|
|
17
18
|
gh api -X PUT repos/RusDavies/moose-inventory/environments/release --input /tmp/moose-env-release.json
|
|
18
|
-
gh api -X POST repos/RusDavies/moose-inventory/environments/release/deployment-branch-policies -f name='v*'
|
|
19
|
+
gh api -X POST repos/RusDavies/moose-inventory/environments/release/deployment-branch-policies -f name='v*' -f type='tag'
|
|
20
|
+
gh api -X DELETE repos/RusDavies/moose-inventory/environments/release/deployment-branch-policies/50646877
|
|
19
21
|
gh api repos/RusDavies/moose-inventory/environments/release/deployment-branch-policies --jq '.'
|
|
22
|
+
gh run rerun 26670139178
|
|
23
|
+
gh api -X POST repos/RusDavies/moose-inventory/actions/runs/26670139178/pending_deployments --input /tmp/moose-approve-deployment.json
|
|
20
24
|
```
|
|
21
25
|
|
|
22
26
|
Confirmed repository/environment:
|
|
@@ -37,7 +41,7 @@ Confirmed repository/environment:
|
|
|
37
41
|
| Prevent self-review | Disabled (`prevent_self_review: false`) | Disabled because OpenClaw/automation pushes use Russ's GitHub account. With `RusDavies` as the only required reviewer, self-review prevention could block Russ from approving a release deployment triggered through his own account. |
|
|
38
42
|
| Wait timer | `0` / none | No arbitrary delay is configured. Human review is the intended release friction. |
|
|
39
43
|
| Deployment branch/tag policy mode | Custom branch policies enabled (`protected_branches: false`, `custom_branch_policies: true`) | The environment uses an explicit allow-list rather than all branches/tags. |
|
|
40
|
-
| Custom deployment policy | `v*`
|
|
44
|
+
| Custom deployment policy | `v*` with `type: tag` | Aligns environment deployment eligibility with the release workflow's `v*` tag trigger. An earlier `type: branch` policy rejected tag `v2.1`; after explicit human approval, it was replaced with a tag policy and verified by the successful `v2.1` release workflow. |
|
|
41
45
|
| Admin bypass | Disabled (`can_admins_bypass: false`) | Admins should not be able to bypass environment protection for this environment through the normal bypass setting. |
|
|
42
46
|
|
|
43
47
|
## Current trusted-publishing path
|
|
@@ -48,7 +52,7 @@ The current release path relies on these controls:
|
|
|
48
52
|
2. The GitHub `release` environment requires review by `RusDavies` before the release job can proceed.
|
|
49
53
|
3. Self-review prevention is disabled so `RusDavies` can approve deployments triggered by automation authenticated as Russ's GitHub account.
|
|
50
54
|
4. Admin bypass is disabled for the `release` environment.
|
|
51
|
-
5. The environment has a custom deployment policy named `v*`.
|
|
55
|
+
5. The environment has a custom tag deployment policy named `v*`.
|
|
52
56
|
6. The release workflow verifies that the tag version matches `Moose::Inventory::VERSION`.
|
|
53
57
|
7. The release workflow installs security tools and runs:
|
|
54
58
|
|
|
@@ -59,11 +63,15 @@ The current release path relies on these controls:
|
|
|
59
63
|
8. RubyGems trusted publishing/OIDC is scoped to repository `RusDavies/moose-inventory`, workflow `release.yml`, and environment `release`.
|
|
60
64
|
9. The package sanity gate verifies the gem payload and executable metadata before publishing.
|
|
61
65
|
|
|
62
|
-
##
|
|
66
|
+
## Tag-policy verification note
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
The first `v2.1` workflow attempt proved that a custom deployment policy named `v*` with API `type: branch` does not permit tag deployments. GitHub rejected the run before any workflow steps executed with:
|
|
65
69
|
|
|
66
|
-
|
|
70
|
+
```text
|
|
71
|
+
Tag "v2.1" is not allowed to deploy to release due to environment protection rules.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
After explicit human approval, the branch policy was replaced with a custom deployment policy named `v*` and `type: tag`. The rerun of workflow `26670139178` for tag `v2.1` then entered the required-review gate, was approved by `RusDavies`, completed the full release workflow successfully, and published `moose-inventory` version `2.1` to RubyGems.
|
|
67
75
|
|
|
68
76
|
## Change-control note
|
|
69
77
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Snapshot Import Availability Fuzz Audit - 2026-05-29
|
|
2
|
+
|
|
3
|
+
Repository: `RusDavies/moose-inventory`
|
|
4
|
+
Local path: `/home/skippy/.openclaw/workspace/projects/moose-inventory`
|
|
5
|
+
Commit audited: `48da0472f8fadb7685777987545d51b4b62eb806`
|
|
6
|
+
Version audited: `2.1`
|
|
7
|
+
Audit context: follow-up to `code-security-audit` run `3`
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
This pass targeted snapshot import availability and malformed-input behavior for the local CLI import path:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ruby -Ilib bin/moose-inventory --config spec/config/config.yml import SNAPSHOT.yml --preview
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The pass used preview mode to keep the fuzz cases non-mutating while still exercising YAML loading, snapshot normalization, validation, and preview generation.
|
|
18
|
+
|
|
19
|
+
## Cases exercised
|
|
20
|
+
|
|
21
|
+
| Case | Result | Notes |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| Baseline valid snapshot | Pass | Preview completed in 0.127s. |
|
|
24
|
+
| Malformed YAML | Graceful rejection | Reported a sanitized `ERROR: Could not parse inventory snapshot ...`. |
|
|
25
|
+
| YAML alias | Ungraceful rejection | Rejected safely, but emitted a Ruby/Psych stack trace. |
|
|
26
|
+
| Disallowed symbol key / duplicate-normalized-key probe | Ungraceful rejection | Rejected safely, but emitted a Ruby/Psych stack trace before project-level validation. |
|
|
27
|
+
| Group cycle | Graceful rejection | Reported `Invalid inventory snapshot: group hierarchy contains a cycle ...`. |
|
|
28
|
+
| Unknown child group reference | Graceful rejection | Reported `Invalid inventory snapshot: group ... references unknown child group ...`. |
|
|
29
|
+
| 5,000 variable entries | Pass | Preview completed in 0.157s. |
|
|
30
|
+
| 250-deep group chain | Pass | Preview completed in 0.142s. |
|
|
31
|
+
| 1,000-deep group chain | Pass | Preview completed in 0.187s. |
|
|
32
|
+
| 3,000-deep group chain | Pass | Preview completed in 0.306s. |
|
|
33
|
+
|
|
34
|
+
Raw local artifact: `.openclaw-security-audit/fuzz_snapshot_import_availability_run.json`.
|
|
35
|
+
|
|
36
|
+
## Finding
|
|
37
|
+
|
|
38
|
+
### P3 / Low: snapshot import does not sanitize all Psych safe-load exceptions
|
|
39
|
+
|
|
40
|
+
`Application#import` currently rescues `Psych::SyntaxError` around `YAML.safe_load_file`, but safe-load can also raise other Psych exceptions such as `Psych::AliasesNotEnabled` and `Psych::DisallowedClass` before Moose Inventory's snapshot validator runs.
|
|
41
|
+
|
|
42
|
+
Observed examples:
|
|
43
|
+
|
|
44
|
+
- YAML alias payload is rejected because aliases are disabled, but the CLI prints a Ruby/Psych stack trace.
|
|
45
|
+
- Symbol-tag payload is rejected because permitted classes are restricted, but the CLI prints a Ruby/Psych stack trace.
|
|
46
|
+
|
|
47
|
+
Security interpretation:
|
|
48
|
+
|
|
49
|
+
- The unsafe payloads were rejected, so this is not unsafe deserialization or code execution.
|
|
50
|
+
- The issue is availability/UX hardening and minor information disclosure through local stack traces and filesystem/gem paths.
|
|
51
|
+
- Reachability is local CLI import of an attacker-supplied or untrusted snapshot file.
|
|
52
|
+
- Priority is P3/low because the blast radius is local command failure/noisy logs rather than inventory corruption or privilege escalation.
|
|
53
|
+
|
|
54
|
+
## Recommended remediation
|
|
55
|
+
|
|
56
|
+
Add a focused backlog item to handle non-syntax `Psych::Exception` failures in the import path with the same sanitized `ERROR: Could not parse inventory snapshot ...` style, and add regression cases for aliases/disallowed classes.
|
|
57
|
+
|
|
58
|
+
A later hardening pass may also consider explicit snapshot size/entity/depth limits, but this fuzz pass did not find practical timeout behavior at the bounded sizes tested.
|
|
@@ -72,6 +72,8 @@ module Moose
|
|
|
72
72
|
puts "Associations added: #{result.associations}"
|
|
73
73
|
rescue Psych::SyntaxError => e
|
|
74
74
|
abort("ERROR: Could not parse inventory snapshot '#{file}': #{e.message}")
|
|
75
|
+
rescue Psych::Exception => e
|
|
76
|
+
abort("ERROR: Could not load inventory snapshot '#{file}': #{e.message}")
|
|
75
77
|
rescue db.exceptions[:moose] => e
|
|
76
78
|
abort("ERROR: #{e.message}")
|
|
77
79
|
end
|
|
@@ -3,6 +3,64 @@
|
|
|
3
3
|
module Moose
|
|
4
4
|
module Inventory
|
|
5
5
|
module Operations
|
|
6
|
+
# Validates group hierarchy cycle safety without recursive traversal.
|
|
7
|
+
class GroupCycleValidator
|
|
8
|
+
def initialize(context:)
|
|
9
|
+
@context = context
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(groups)
|
|
13
|
+
visiting = {}
|
|
14
|
+
visited = {}
|
|
15
|
+
|
|
16
|
+
groups.each_key do |root|
|
|
17
|
+
validate_from_root!(root, groups, visiting, visited)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :context
|
|
24
|
+
|
|
25
|
+
def validate_from_root!(root, groups, visiting, visited)
|
|
26
|
+
stack = [[root, false]]
|
|
27
|
+
until stack.empty?
|
|
28
|
+
name, expanded = stack.pop
|
|
29
|
+
next if visited[name]
|
|
30
|
+
|
|
31
|
+
if expanded
|
|
32
|
+
visiting.delete(name)
|
|
33
|
+
visited[name] = true
|
|
34
|
+
else
|
|
35
|
+
queue_group_children!(name, groups, visiting, stack)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def queue_group_children!(name, groups, visiting, stack)
|
|
41
|
+
raise_invalid("group hierarchy contains a cycle at '#{name}'") if visiting[name]
|
|
42
|
+
|
|
43
|
+
visiting[name] = true
|
|
44
|
+
stack << [name, true]
|
|
45
|
+
array_value(groups[name], 'children', label: "group '#{name}' children").reverse_each do |child_name|
|
|
46
|
+
raise_invalid("group hierarchy contains a cycle at '#{child_name}'") if visiting[child_name]
|
|
47
|
+
|
|
48
|
+
stack << [child_name, false]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def array_value(payload, key, label:)
|
|
53
|
+
value = payload.fetch(key, [])
|
|
54
|
+
raise_invalid("#{label} must be a list") unless value.is_a?(Array)
|
|
55
|
+
|
|
56
|
+
value.map(&:to_s)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def raise_invalid(message)
|
|
60
|
+
raise context.moose_exception_class, "Invalid inventory snapshot: #{message}."
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
6
64
|
# Normalizes and validates portable inventory snapshot input before import.
|
|
7
65
|
class InventorySnapshotValidator
|
|
8
66
|
def initialize(context:)
|
|
@@ -73,25 +131,7 @@ module Moose
|
|
|
73
131
|
end
|
|
74
132
|
|
|
75
133
|
def validate_group_cycles!(groups)
|
|
76
|
-
|
|
77
|
-
visited = {}
|
|
78
|
-
|
|
79
|
-
groups.each_key do |name|
|
|
80
|
-
visit_group!(name, groups, visiting, visited)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def visit_group!(name, groups, visiting, visited)
|
|
85
|
-
return if visited[name]
|
|
86
|
-
|
|
87
|
-
raise_invalid("group hierarchy contains a cycle at '#{name}'") if visiting[name]
|
|
88
|
-
|
|
89
|
-
visiting[name] = true
|
|
90
|
-
array_value(groups[name], 'children', label: "group '#{name}' children").each do |child_name|
|
|
91
|
-
visit_group!(child_name, groups, visiting, visited)
|
|
92
|
-
end
|
|
93
|
-
visiting.delete(name)
|
|
94
|
-
visited[name] = true
|
|
134
|
+
GroupCycleValidator.new(context: context).call(groups)
|
|
95
135
|
end
|
|
96
136
|
|
|
97
137
|
def array_value(payload, key, label:)
|
|
@@ -65,4 +65,7 @@ else
|
|
|
65
65
|
exit 0
|
|
66
66
|
fi
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
# osv-scanner 2.x treats --lockfile as an explicit scan target; adding the
|
|
69
|
+
# repository path as a positional source makes it try to infer an extractor for
|
|
70
|
+
# Gemfile.lock through the directory scanner and fail with exit 127.
|
|
71
|
+
"${OSV_SCANNER[@]}" scan source --lockfile Gemfile.lock
|
|
@@ -96,5 +96,37 @@ RSpec.describe Moose::Inventory::Cli::Application do
|
|
|
96
96
|
expect(@db.models[:host].count).to eq(0)
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
|
+
|
|
100
|
+
it 'sanitizes alias rejection errors while previewing snapshot import' do
|
|
101
|
+
Dir.mktmpdir do |dir|
|
|
102
|
+
path = File.join(dir, 'inventory.yml')
|
|
103
|
+
File.write(path, "---\nversion: 1\ngroups: &groups {}\nhosts: *groups\n")
|
|
104
|
+
|
|
105
|
+
actual = runner { @app.start(['import', path, '--preview']) }
|
|
106
|
+
|
|
107
|
+
expect(actual[:aborted]).to eq(true)
|
|
108
|
+
expect(actual[:STDERR]).to include("ERROR: Could not load inventory snapshot '#{path}':")
|
|
109
|
+
expect(actual[:STDERR]).to include('Alias parsing was not enabled')
|
|
110
|
+
expect(actual[:STDERR]).not_to include('/psych/')
|
|
111
|
+
expect(actual[:STDERR]).not_to include('from ')
|
|
112
|
+
expect(@db.models[:host].count).to eq(0)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'sanitizes disallowed class errors while previewing snapshot import' do
|
|
117
|
+
Dir.mktmpdir do |dir|
|
|
118
|
+
path = File.join(dir, 'inventory.yml')
|
|
119
|
+
File.write(path, "---\nversion: 1\ngroups:\n g: {}\n :g: {}\nhosts: {}\n")
|
|
120
|
+
|
|
121
|
+
actual = runner { @app.start(['import', path, '--preview']) }
|
|
122
|
+
|
|
123
|
+
expect(actual[:aborted]).to eq(true)
|
|
124
|
+
expect(actual[:STDERR]).to include("ERROR: Could not load inventory snapshot '#{path}':")
|
|
125
|
+
expect(actual[:STDERR]).to include('Tried to load unspecified class: Symbol')
|
|
126
|
+
expect(actual[:STDERR]).not_to include('/psych/')
|
|
127
|
+
expect(actual[:STDERR]).not_to include('from ')
|
|
128
|
+
expect(@db.models[:host].count).to eq(0)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
99
131
|
end
|
|
100
132
|
# rubocop:enable Metrics/BlockLength
|
|
@@ -222,5 +222,18 @@ RSpec.describe Moose::Inventory::Operations::ImportInventorySnapshot, '#preview'
|
|
|
222
222
|
expect(@db.models[:group].where(name: 'blue').count).to eq(0)
|
|
223
223
|
expect(group.groupvars_dataset[name: 'role'][:value]).to eq('old')
|
|
224
224
|
end
|
|
225
|
+
|
|
226
|
+
it 'previews deep acyclic group chains without recursive validator stack exhaustion' do
|
|
227
|
+
groups = (1..5_000).to_h do |index|
|
|
228
|
+
child = index < 5_000 ? ["group#{index + 1}"] : []
|
|
229
|
+
["group#{index}", { children: child, vars: {} }]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
preview = operation.preview(snapshot: { version: 1, hosts: {}, groups: groups })
|
|
233
|
+
|
|
234
|
+
expect(preview['changes_applied']).to eq(false)
|
|
235
|
+
expect(preview.dig('summary', 'groups_created')).to eq(5_000)
|
|
236
|
+
expect(@db.models[:group].count).to eq(0)
|
|
237
|
+
end
|
|
225
238
|
end
|
|
226
239
|
# rubocop:enable Metrics/BlockLength
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moose-inventory
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Russell Davies
|
|
@@ -307,6 +307,7 @@ files:
|
|
|
307
307
|
- docs/security-audit-2026-05-21.md
|
|
308
308
|
- docs/security-audit-2026-05-26-rerun.md
|
|
309
309
|
- docs/security-audit-2026-05-26.md
|
|
310
|
+
- docs/security-audit-2026-05-29-snapshot-import-fuzz.md
|
|
310
311
|
- docs/security/accepted-risk-register.md
|
|
311
312
|
- docs/security/security-privacy-process.md
|
|
312
313
|
- docs/ux/cli-workflow-notes.md
|