moose-inventory 1.0.8 → 2.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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +49 -0
- data/.github/workflows/release.yml +58 -0
- data/.gitignore +1 -1
- data/.gitleaks.toml +9 -0
- data/.rubocop.yml +19 -784
- data/BACKLOG.md +290 -0
- data/Gemfile.lock +95 -0
- data/README.md +38 -9
- data/Rakefile +1 -1
- data/bin/moose-inventory +1 -1
- data/docs/release/publishing.md +109 -0
- data/docs/release/release-readiness.md +55 -0
- data/docs/security-audit-2026-05-21.md +71 -0
- data/docs/security-audit-2026-05-26-rerun.md +75 -0
- data/docs/security-audit-2026-05-26.md +63 -0
- data/lib/moose_inventory/cli/formatter.rb +16 -17
- data/lib/moose_inventory/cli/group.rb +4 -1
- data/lib/moose_inventory/cli/group_add.rb +89 -75
- data/lib/moose_inventory/cli/group_addchild.rb +84 -71
- data/lib/moose_inventory/cli/group_addhost.rb +78 -69
- data/lib/moose_inventory/cli/group_addvar.rb +37 -37
- data/lib/moose_inventory/cli/group_get.rb +23 -26
- data/lib/moose_inventory/cli/group_list.rb +12 -15
- data/lib/moose_inventory/cli/group_listvars.rb +12 -14
- data/lib/moose_inventory/cli/group_rm.rb +104 -76
- data/lib/moose_inventory/cli/group_rmchild.rb +99 -54
- data/lib/moose_inventory/cli/group_rmhost.rb +64 -60
- data/lib/moose_inventory/cli/group_rmvar.rb +5 -5
- data/lib/moose_inventory/cli/helpers.rb +76 -0
- data/lib/moose_inventory/cli/host.rb +4 -1
- data/lib/moose_inventory/cli/host_add.rb +51 -66
- data/lib/moose_inventory/cli/host_addgroup.rb +77 -68
- data/lib/moose_inventory/cli/host_addvar.rb +6 -6
- data/lib/moose_inventory/cli/host_get.rb +15 -18
- data/lib/moose_inventory/cli/host_list.rb +3 -3
- data/lib/moose_inventory/cli/host_listvars.rb +21 -23
- data/lib/moose_inventory/cli/host_rm.rb +9 -9
- data/lib/moose_inventory/cli/host_rmgroup.rb +63 -60
- data/lib/moose_inventory/cli/host_rmvar.rb +3 -3
- data/lib/moose_inventory/config/config.rb +43 -40
- data/lib/moose_inventory/db/db.rb +92 -52
- data/lib/moose_inventory/db/models.rb +11 -12
- data/lib/moose_inventory/inventory_context.rb +50 -0
- data/lib/moose_inventory/operations/add_associations.rb +127 -0
- data/lib/moose_inventory/operations/add_groups.rb +115 -0
- data/lib/moose_inventory/operations/add_hosts.rb +110 -0
- data/lib/moose_inventory/operations/group_child_relations.rb +118 -0
- data/lib/moose_inventory/operations/group_cleanup.rb +55 -0
- data/lib/moose_inventory/operations/remove_associations.rb +101 -0
- data/lib/moose_inventory/operations/remove_groups.rb +79 -0
- data/lib/moose_inventory/version.rb +1 -1
- data/moose-inventory.gemspec +38 -20
- data/scripts/check.sh +10 -0
- data/scripts/ci/check_permissions.sh +35 -0
- data/scripts/ci/check_rubocop.sh +28 -0
- data/scripts/ci/check_secrets.sh +26 -0
- data/scripts/ci/check_security.sh +68 -0
- data/scripts/ci/install_security_tools.sh +47 -0
- data/scripts/ci/package_sanity.sh +46 -0
- data/scripts/files.rb +1 -4
- data/scripts/install_dependencies.sh +19 -0
- data/scripts/reports.sh +2 -2
- data/spec/lib/moose_inventory/cli/cli_spec.rb +13 -14
- data/spec/lib/moose_inventory/cli/group_add_spec.rb +118 -119
- data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +49 -51
- data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +80 -83
- data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +91 -91
- data/spec/lib/moose_inventory/cli/group_get_spec.rb +22 -23
- data/spec/lib/moose_inventory/cli/group_list_spec.rb +19 -20
- data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +35 -36
- data/spec/lib/moose_inventory/cli/group_rm_spec.rb +115 -78
- data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +86 -45
- data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +43 -46
- data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +131 -131
- data/spec/lib/moose_inventory/cli/group_spec.rb +9 -9
- data/spec/lib/moose_inventory/cli/host_add_spec.rb +103 -43
- data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +78 -80
- data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +122 -122
- data/spec/lib/moose_inventory/cli/host_get_spec.rb +16 -16
- data/spec/lib/moose_inventory/cli/host_list_spec.rb +8 -8
- data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +50 -52
- data/spec/lib/moose_inventory/cli/host_rm_spec.rb +12 -12
- data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +48 -51
- data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +136 -136
- data/spec/lib/moose_inventory/config/config_spec.rb +16 -3
- data/spec/lib/moose_inventory/db/db_spec.rb +386 -2
- data/spec/lib/moose_inventory/db/models_spec.rb +10 -11
- data/spec/lib/moose_inventory/operations/add_associations_spec.rb +77 -0
- data/spec/lib/moose_inventory/operations/add_groups_spec.rb +65 -0
- data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +69 -0
- data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +76 -0
- data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +78 -0
- data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +57 -0
- data/spec/shared/shared_config_setup.rb +2 -2
- data/spec/spec_helper.rb +7 -8
- metadata +157 -105
- data/.coveralls.yml +0 -0
- data/Guardfile +0 -38
- data/config/dotfiles/coveralls.yml +0 -0
- data/config/dotfiles/gitignore +0 -20
- data/config/dotfiles/rubocop.yml +0 -793
- data/scripts/guard_quality.sh +0 -3
- data/scripts/guard_test.sh +0 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Security audit — 2026-05-21
|
|
2
|
+
|
|
3
|
+
Scope: local static/security review of the `moose-inventory` Ruby CLI/gem at commit `ff522502d5981314c451e855be10cbbc7ebeba48`, plus the hardening changes on branch `security-audit-2026-05-21`.
|
|
4
|
+
|
|
5
|
+
## Executive summary
|
|
6
|
+
|
|
7
|
+
The audit found one actionable dependency vulnerability and one low-risk repository hygiene issue. Both were remediated in this branch:
|
|
8
|
+
|
|
9
|
+
1. Development dependency `rake 10.5.0` was affected by CVE-2020-8130 / GHSA-jppv-gw3r-w3q8, an OS command injection issue in `Rake::FileList` for filenames beginning with `|`. The gemspec now requires `rake >= 13.0, < 14`, and `Gemfile.lock` resolves `rake 13.4.2`.
|
|
10
|
+
2. Most source, config, docs, and spec files were executable (`100755`) even though they are not entrypoints. The branch normalizes non-executable files to `100644`, keeping only `bin/moose-inventory` and actual scripts executable.
|
|
11
|
+
|
|
12
|
+
After the dependency update, an OSV query for locked RubyGems dependencies returned zero known vulnerabilities. Semgrep Ruby rules returned zero findings.
|
|
13
|
+
|
|
14
|
+
## Surfaces reviewed
|
|
15
|
+
|
|
16
|
+
- CLI entrypoint: `bin/moose-inventory`
|
|
17
|
+
- Global config parsing and file loading: `lib/moose_inventory/config/config.rb`
|
|
18
|
+
- DB connection and schema creation: `lib/moose_inventory/db/db.rb`
|
|
19
|
+
- Sequel models and associations: `lib/moose_inventory/db/models.rb`
|
|
20
|
+
- CLI command handlers under `lib/moose_inventory/cli/`
|
|
21
|
+
- Packaging metadata: `moose-inventory.gemspec`, `Gemfile`, `Gemfile.lock`
|
|
22
|
+
- Helper scripts under `scripts/`
|
|
23
|
+
- Test fixtures/config under `spec/`
|
|
24
|
+
|
|
25
|
+
## Findings fixed
|
|
26
|
+
|
|
27
|
+
### P2 — Vulnerable development dependency: `rake 10.5.0`
|
|
28
|
+
|
|
29
|
+
- Evidence: `Gemfile.lock` resolved `rake (10.5.0)` and `moose-inventory.gemspec` constrained rake to `~> 10`.
|
|
30
|
+
- Advisory: OSV/GitHub Advisory `GHSA-jppv-gw3r-w3q8`, CVE-2020-8130.
|
|
31
|
+
- Impact: local OS command injection in vulnerable Rake versions when `Rake::FileList` receives a filename beginning with the pipe character (`|`). This is primarily a developer/build-time risk, not a runtime CLI inventory risk.
|
|
32
|
+
- Fix: changed the development dependency to `rake >= 13.0, < 14` and refreshed `Gemfile.lock` to `rake 13.4.2`.
|
|
33
|
+
- Validation: OSV query after the update returned `deps_with_vulns 0`.
|
|
34
|
+
|
|
35
|
+
### P4 — Over-broad executable bits on repository files
|
|
36
|
+
|
|
37
|
+
- Evidence: file inventory showed `100755` executable mode on `Gemfile`, `README.md`, library files, specs, config, and other non-entrypoint files.
|
|
38
|
+
- Impact: low. This does not create a direct vulnerability by itself, but it expands accidental execution surface and makes packaging/review noisier than necessary. Tiny footgun, sharp enough to remove.
|
|
39
|
+
- Fix: normalized non-entrypoint files to `100644`; retained executable mode for `bin/moose-inventory` and scripts with shebangs.
|
|
40
|
+
|
|
41
|
+
## Notable negative findings
|
|
42
|
+
|
|
43
|
+
- Config deserialization now uses `YAML.safe_load_file` with aliases disabled and no permitted classes/symbols.
|
|
44
|
+
- No shell execution sinks were found in runtime code. The backtick use in `moose-inventory.gemspec` is the normal `git ls-files` packaging pattern.
|
|
45
|
+
- Database access uses Sequel model/hash APIs for user-provided names and variables; no raw SQL interpolation was identified in the reviewed CLI/database paths.
|
|
46
|
+
- No committed secrets were identified outside expected example/test placeholder passwords.
|
|
47
|
+
- No external network-facing service, HTTP route, RPC handler, webhook, queue consumer, or upload parser exists in this repo; the meaningful attack surface is local CLI usage and build/development tooling.
|
|
48
|
+
|
|
49
|
+
## Tooling evidence
|
|
50
|
+
|
|
51
|
+
- Inventory: 79 files; Ruby manifests: `Gemfile`, `Gemfile.lock`.
|
|
52
|
+
- Semgrep: `semgrep --config p/ruby --json --quiet .` returned 0 findings.
|
|
53
|
+
- OSV dependency query before fix: 1 vulnerable dependency (`rake 10.5.0`, `GHSA-jppv-gw3r-w3q8`).
|
|
54
|
+
- OSV dependency query after fix: 41 RubyGems dependency records queried, 0 dependencies with known vulnerabilities.
|
|
55
|
+
- `bundle-audit`, `osv-scanner`, `gitleaks`, `trufflehog`, and `brakeman` were not installed in this environment. Brakeman is also Rails-specific and not expected to apply here.
|
|
56
|
+
|
|
57
|
+
## Residual risks / future hardening
|
|
58
|
+
|
|
59
|
+
- Consider adding a CI security job for OSV or bundler-audit so dependency advisories are caught before they fossilize in the lockfile like a tiny Jurassic Park exhibit.
|
|
60
|
+
- Consider keeping generated coverage artifacts out of normal grep/scanner paths; they are ignored from this audit because they contain large bundled HTML/CSS assets.
|
|
61
|
+
|
|
62
|
+
## GitHub Dependabot follow-up after first push
|
|
63
|
+
|
|
64
|
+
After pushing the audit remediation, GitHub emitted a default-branch warning for 7 Dependabot vulnerabilities. Querying the repository Dependabot alerts through `gh api repos/RusDavies/moose-inventory/dependabot/alerts` showed that all 7 were already in `fixed` state after the modernization/security-audit commits reached GitHub:
|
|
65
|
+
|
|
66
|
+
- `rake`: GHSA-jppv-gw3r-w3q8 / CVE-2020-8130, fixed by `rake >= 13.0, < 14` and lockfile `rake 13.4.2`.
|
|
67
|
+
- `json`: GHSA-jphg-qwrw-7w9g / CVE-2020-10663, fixed by the existing current constraint `json >= 2.7, < 3` and lockfile `json 2.19.5`.
|
|
68
|
+
- `rubocop`: GHSA-wmjf-jpjj-9f3j / CVE-2017-8418, fixed by removing RuboCop as a development dependency.
|
|
69
|
+
- `bundler`: GHSA-jvgm-pfqv-887x / CVE-2016-7954, GHSA-g98m-96g9-wfjq / CVE-2019-3881, GHSA-fp4w-jxhp-m23p / CVE-2020-36327, and GHSA-fj7f-vq84-fh43 / CVE-2021-43809. GitHub marked these fixed because the lockfile uses Bundler 2.6.9; this follow-up also tightens the gemspec development dependency to `bundler >= 2.2.33, < 3` so fresh development installs cannot select known-vulnerable Bundler 1.x/early 2.x releases.
|
|
70
|
+
|
|
71
|
+
Follow-up validation: `gh api 'repos/RusDavies/moose-inventory/dependabot/alerts?state=open'` returned no open alerts.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Security Audit Rerun — 2026-05-26
|
|
2
|
+
|
|
3
|
+
Repository: `RusDavies/moose-inventory`
|
|
4
|
+
Local path: `/home/skippy/.openclaw/workspace/projects/moose-inventory`
|
|
5
|
+
Audited commit at start: `a07b5c89214a3cee66170217c5b38e9ad2ae093a`
|
|
6
|
+
Audit branch: `security-audit-2026-05-26-rerun`
|
|
7
|
+
Evidence store: `.openclaw-security-audit/audit.sqlite`, audit run `2`
|
|
8
|
+
|
|
9
|
+
## Executive summary
|
|
10
|
+
|
|
11
|
+
The rerun found no exploitable application vulnerabilities in the Ruby CLI/config/database code reviewed, and all deterministic dependency, advisory, package, and secret-scanning gates passed.
|
|
12
|
+
|
|
13
|
+
One release-supply-chain hardening gap was identified and fixed during the audit: the release workflow ran `./scripts/check.sh` without installing or requiring the dedicated security tools, so a tag-based release could publish even if `gitleaks`, `osv-scanner`, or `bundler-audit` coverage was absent from that release job. CI already enforced those tools; release now does too.
|
|
14
|
+
|
|
15
|
+
## Scope
|
|
16
|
+
|
|
17
|
+
Reviewed security-relevant surfaces and changes since the prior audit:
|
|
18
|
+
|
|
19
|
+
- GitHub Actions CI and release workflows.
|
|
20
|
+
- Security-tool installation and enforcement scripts.
|
|
21
|
+
- Ruby CLI entrypoints and Thor command surfaces.
|
|
22
|
+
- YAML configuration loading.
|
|
23
|
+
- SQLite/MySQL/PostgreSQL connection setup and password handling.
|
|
24
|
+
- Recursive group deletion behavior touched by recent issue work.
|
|
25
|
+
- Gem packaging/release path.
|
|
26
|
+
|
|
27
|
+
## Deterministic results
|
|
28
|
+
|
|
29
|
+
- Full local required-tool gate: passed.
|
|
30
|
+
- RSpec: 268 examples, 0 failures.
|
|
31
|
+
- Coverage: 96.52% line coverage.
|
|
32
|
+
- Custom OSV dependency check: 45 dependencies queried, 0 vulnerable.
|
|
33
|
+
- `bundler-audit`: no vulnerabilities found.
|
|
34
|
+
- `osv-scanner`: no issues found in `Gemfile.lock`.
|
|
35
|
+
- `gitleaks`: dedicated secret scan passed.
|
|
36
|
+
- Package sanity: built and inspected `tmp/pkg/moose-inventory.gem` successfully.
|
|
37
|
+
- Semgrep Ruby registry scan: 62 tracked Ruby files scanned with 44 Ruby rules, 0 findings.
|
|
38
|
+
- GitHub Dependabot open alerts: 0.
|
|
39
|
+
- GitHub code scanning alerts: unavailable / no analysis found (`404`).
|
|
40
|
+
- GitHub secret scanning alerts: unavailable because secret scanning is disabled for this repository.
|
|
41
|
+
- Workflow YAML parse check: `ci.yml` and `release.yml` parsed successfully with Ruby Psych.
|
|
42
|
+
- Current GitHub CI before audit branch: latest `master` CI run succeeded for Ruby 3.2, 3.3, and 3.4.
|
|
43
|
+
|
|
44
|
+
## Finding fixed during audit
|
|
45
|
+
|
|
46
|
+
### SEC-RERUN-2026-05-26-01 — Release workflow did not require dedicated security tools
|
|
47
|
+
|
|
48
|
+
- Priority before fix: P2 medium, release supply-chain hardening.
|
|
49
|
+
- Exposure: tag-triggered release workflow.
|
|
50
|
+
- Affected file: `.github/workflows/release.yml`.
|
|
51
|
+
- Evidence: CI installed `gitleaks`/`osv-scanner` and set `MOOSE_INVENTORY_REQUIRE_SECURITY_TOOLS=1`, but the release workflow only ran `./scripts/check.sh`. In local mode, `scripts/ci/check_security.sh` and `scripts/ci/check_secrets.sh` intentionally skip missing optional security tools unless `MOOSE_INVENTORY_REQUIRE_SECURITY_TOOLS=1` is set.
|
|
52
|
+
- Impact: a release tag created from an unexpected commit or during a tooling/path issue could publish without the same dedicated SCA/secret-scan enforcement as CI.
|
|
53
|
+
- Fix applied: release workflow now sets up Go with cache disabled, installs the pinned security CLIs via `scripts/ci/install_security_tools.sh`, runs native dependency installation with a 5-minute timeout, and runs `./scripts/check.sh` with `MOOSE_INVENTORY_REQUIRE_SECURITY_TOOLS=1`.
|
|
54
|
+
- Verification: full local required-tool gate passed after the workflow change.
|
|
55
|
+
- Residual risk: release workflow can only be fully proven on the next real release tag because already-published `v1.0.9` must not be retagged.
|
|
56
|
+
|
|
57
|
+
## Reviewed areas with no actionable finding
|
|
58
|
+
|
|
59
|
+
- YAML config loading uses `YAML.safe_load_file` with aliases disabled and no permitted classes/symbols.
|
|
60
|
+
- SQLite database path handling creates parent directories with `FileUtils.mkdir_p`; this is local config-driven CLI behavior, not a remotely reachable path traversal surface.
|
|
61
|
+
- MySQL/PostgreSQL password handling supports `password_env`; plaintext `password` remains for compatibility but README guidance discourages committing it.
|
|
62
|
+
- CLI input reaches Sequel model operations rather than shell execution; no shell/eval sink was identified in application code.
|
|
63
|
+
- Recent recursive group deletion is explicit opt-in and keeps host fallback behavior covered by regression tests.
|
|
64
|
+
- GitHub Actions release job uses OIDC/trusted publishing and does not store a RubyGems API key in the workflow.
|
|
65
|
+
|
|
66
|
+
## Limitations
|
|
67
|
+
|
|
68
|
+
- This was a local/source and CI/release workflow audit, not an active test against live external databases or RubyGems publishing.
|
|
69
|
+
- GitHub code scanning is not configured, so there were no CodeQL/code-scanning results to review.
|
|
70
|
+
- GitHub secret scanning is disabled for the repository; local `gitleaks` coverage was used instead.
|
|
71
|
+
- The release trusted-publishing path still needs verification on the next real version tag.
|
|
72
|
+
|
|
73
|
+
## Conclusion
|
|
74
|
+
|
|
75
|
+
No open exploitable vulnerabilities remain from this rerun. The only identified security gap was release-pipeline parity with CI security tooling, and it was fixed in this audit branch.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Security audit — 2026-05-26
|
|
2
|
+
|
|
3
|
+
Scope: local static/security review of the `moose-inventory` Ruby CLI/gem at commit `8c3eaada5d70ef599961b8ca8b78e12ea4ce83c9` on branch `security-audit-2026-05-26`.
|
|
4
|
+
|
|
5
|
+
## Executive summary
|
|
6
|
+
|
|
7
|
+
No actionable security vulnerabilities were identified in this pass.
|
|
8
|
+
|
|
9
|
+
The meaningful attack surface remains local CLI execution, configuration-file loading, database access through Sequel, package/release automation, and developer tooling. There is no HTTP server, RPC endpoint, webhook handler, queue consumer, file upload parser, shell-command execution path, or plugin system in this repository.
|
|
10
|
+
|
|
11
|
+
The areas remediated in the prior 2026-05-21 audit remain in good shape: YAML config loading uses `YAML.safe_load_file`, DB credentials can be supplied through environment variables, OSV reports no known vulnerable locked RubyGems dependencies, CI/package sanity gates are present, and GitHub Dependabot has no open alerts.
|
|
12
|
+
|
|
13
|
+
## Surfaces reviewed
|
|
14
|
+
|
|
15
|
+
- CLI entrypoint: `bin/moose-inventory`
|
|
16
|
+
- Global option parsing and config loading: `lib/moose_inventory/config/config.rb`
|
|
17
|
+
- DB connection/schema/transaction code: `lib/moose_inventory/db/db.rb`
|
|
18
|
+
- Sequel models and associations: `lib/moose_inventory/db/models.rb`
|
|
19
|
+
- CLI command handlers under `lib/moose_inventory/cli/`
|
|
20
|
+
- Formatter/output serialization: `lib/moose_inventory/cli/formatter.rb`
|
|
21
|
+
- Packaging and release metadata: `moose-inventory.gemspec`, `Gemfile`, `Gemfile.lock`, `.github/workflows/ci.yml`, `.github/workflows/release.yml`
|
|
22
|
+
- Helper scripts under `scripts/`
|
|
23
|
+
- Test config/spec fixtures under `spec/`
|
|
24
|
+
|
|
25
|
+
## Findings
|
|
26
|
+
|
|
27
|
+
No P0/P1/P2/P3 actionable findings were identified.
|
|
28
|
+
|
|
29
|
+
## Notable negative findings
|
|
30
|
+
|
|
31
|
+
- Config deserialization uses `YAML.safe_load_file` with aliases disabled and no permitted classes/symbols.
|
|
32
|
+
- No runtime shell execution sinks were identified.
|
|
33
|
+
- Database access uses Sequel model/dataset/hash APIs for user-controlled names, groups, hosts, and variables; no raw SQL interpolation was identified in reviewed runtime paths.
|
|
34
|
+
- MySQL/PostgreSQL passwords can be supplied via `password_env`, and README guidance prefers environment-backed passwords over plaintext config values.
|
|
35
|
+
- No committed secrets were identified outside expected example/test placeholders.
|
|
36
|
+
- GitHub Actions release publishing uses RubyGems trusted publishing/OIDC rather than a stored RubyGems API key.
|
|
37
|
+
- Dependency advisory gate queried OSV for 41 locked RubyGems dependency records and reported zero known vulnerabilities.
|
|
38
|
+
- GitHub Dependabot open-alert query returned zero open alerts.
|
|
39
|
+
|
|
40
|
+
## Tooling evidence
|
|
41
|
+
|
|
42
|
+
- Audit evidence store initialized at `.openclaw-security-audit/audit.sqlite` with `audit_run_id=1`.
|
|
43
|
+
- Inventory: 187 files; Ruby manifests detected: `Gemfile`, `Gemfile.lock`, plus generated package-sanity manifests under `tmp/package-sanity`.
|
|
44
|
+
- Symbol extractor scanned 156 files but did not extract Ruby symbols/surfaces with the current lightweight extractor.
|
|
45
|
+
- Semgrep auto-config failed because metrics are disabled; reran explicit Ruby registry rules instead.
|
|
46
|
+
- Semgrep: `semgrep --config p/ruby --json --metrics=off --exclude .openclaw-security-audit --exclude spec/reports --exclude tmp .` scanned 62 tracked Ruby files with 44 rules and returned 0 findings.
|
|
47
|
+
- Dependency advisory gate: `./scripts/ci/check_security.sh` queried 41 RubyGems dependencies and returned 0 vulnerable dependencies.
|
|
48
|
+
- Full local gate: `./scripts/check.sh` passed with 268 examples, 0 failures, 96.52% line coverage, OSV 0 vulnerabilities, and package sanity passed.
|
|
49
|
+
- GitHub Dependabot: `gh api 'repos/RusDavies/moose-inventory/dependabot/alerts?state=open' --jq 'length'` returned `0`.
|
|
50
|
+
- GitHub code-scanning alerts could not be queried because no code-scanning analysis exists for this repository; GitHub returned `404 no analysis found`.
|
|
51
|
+
|
|
52
|
+
## Tooling limitations
|
|
53
|
+
|
|
54
|
+
- `osv-scanner`, `bundler-audit`/`bundle-audit`, `gitleaks`, `trufflehog`, `brakeman`, `flog`, and `reek` were not installed in this environment.
|
|
55
|
+
- `bundle exec rubocop` could not run because RuboCop is not part of the bundle. This is not a security gate failure, but it limits style/static-quality coverage.
|
|
56
|
+
- Secret scanning was limited to tracked-file grep patterns because dedicated secret scanners were unavailable.
|
|
57
|
+
- The audit did not perform active exploitation against external systems or live database servers.
|
|
58
|
+
|
|
59
|
+
## Residual risks / recommendations
|
|
60
|
+
|
|
61
|
+
- Consider adding a dedicated secret scanner such as `gitleaks` or `trufflehog` to local/CI security tooling if this project will accept outside contributions.
|
|
62
|
+
- Consider adding `bundler-audit` or `osv-scanner` as an optional developer tool if broader advisory coverage is desired beyond the existing custom OSV gate.
|
|
63
|
+
- Keep generated coverage and package-sanity artifacts excluded from security scans; they are noisy and not part of the runtime gem surface.
|
|
@@ -15,37 +15,37 @@ module Moose
|
|
|
15
15
|
def self.dump(arg, format = nil)
|
|
16
16
|
out(arg, format)
|
|
17
17
|
end
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
def self.out(arg, format = nil)
|
|
20
20
|
return if arg.nil?
|
|
21
21
|
|
|
22
22
|
if format.nil?
|
|
23
23
|
format = Moose::Inventory::Config._confopts[:format].downcase
|
|
24
24
|
end
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
case format
|
|
27
|
-
when 'yaml','y'
|
|
27
|
+
when 'yaml', 'y'
|
|
28
28
|
$stdout.puts arg.to_yaml
|
|
29
29
|
|
|
30
|
-
when 'prettyjson','pjson','p'
|
|
30
|
+
when 'prettyjson', 'pjson', 'p'
|
|
31
31
|
$stdout.puts JSON.pretty_generate(arg)
|
|
32
32
|
|
|
33
|
-
when 'json','j'
|
|
33
|
+
when 'json', 'j'
|
|
34
34
|
$stdout.puts arg.to_json
|
|
35
35
|
|
|
36
36
|
else
|
|
37
37
|
abort("Output format '#{format}' is not yet supported.")
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
#---------------
|
|
42
42
|
attr_accessor :indent
|
|
43
43
|
|
|
44
44
|
def reset_indent
|
|
45
45
|
@indent = 2
|
|
46
46
|
end
|
|
47
|
-
|
|
48
|
-
def puts(indent, msg, stream='STDOUT')
|
|
47
|
+
|
|
48
|
+
def puts(indent, msg, stream = 'STDOUT')
|
|
49
49
|
case stream
|
|
50
50
|
when 'STDOUT'
|
|
51
51
|
$stdout.puts msg.indent(indent)
|
|
@@ -55,8 +55,8 @@ module Moose
|
|
|
55
55
|
abort("Output stream '#{stream}' is not known.")
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
|
-
|
|
59
|
-
def print(indent, msg, stream='STDOUT')
|
|
58
|
+
|
|
59
|
+
def print(indent, msg, stream = 'STDOUT')
|
|
60
60
|
case stream
|
|
61
61
|
when 'STDOUT'
|
|
62
62
|
$stdout.print msg.indent(indent)
|
|
@@ -67,25 +67,24 @@ module Moose
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
def info(
|
|
70
|
+
def info(_indent, _msg, stream = 'STDOUT')
|
|
71
71
|
case stream
|
|
72
72
|
when 'STDOUT'
|
|
73
|
-
$stdout.print
|
|
73
|
+
$stdout.print 'INFO: {msg}'
|
|
74
74
|
when 'STDERR'
|
|
75
|
-
$stderr.print
|
|
75
|
+
$stderr.print 'INFO: {msg}'
|
|
76
76
|
else
|
|
77
77
|
abort("Output stream '#{stream}' is not known.")
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
def warn(msg)
|
|
82
|
-
|
|
82
|
+
$stderr.print "WARNING: #{msg}"
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def error(msg)
|
|
86
|
-
|
|
86
|
+
$stderr.print "ERROR: #{msg}"
|
|
87
87
|
end
|
|
88
|
-
|
|
89
88
|
end
|
|
90
89
|
end
|
|
91
90
|
end
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
require 'thor'
|
|
2
2
|
require_relative './formatter.rb'
|
|
3
|
+
require_relative './helpers.rb'
|
|
3
4
|
|
|
4
5
|
module Moose
|
|
5
6
|
module Inventory
|
|
6
7
|
module Cli
|
|
7
8
|
##
|
|
8
9
|
# Class implementing the "group" methods of the CLI
|
|
9
|
-
class Group < Thor
|
|
10
|
+
class Group < Thor
|
|
11
|
+
include Moose::Inventory::Cli::Helpers
|
|
12
|
+
|
|
10
13
|
require_relative 'group_add'
|
|
11
14
|
require_relative 'group_get'
|
|
12
15
|
require_relative 'group_list'
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative 'formatter'
|
|
5
|
+
require_relative '../inventory_context'
|
|
6
|
+
require_relative '../operations/add_groups'
|
|
3
7
|
|
|
4
8
|
module Moose
|
|
5
9
|
module Inventory
|
|
@@ -10,89 +14,99 @@ module Moose
|
|
|
10
14
|
#==========================
|
|
11
15
|
desc 'add NAME', 'Add a group NAME to the inventory'
|
|
12
16
|
option :hosts
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# rubocop:enable Metrics/LineLength
|
|
16
|
-
if argv.length < 1
|
|
17
|
-
abort("ERROR: Wrong number of arguments, #{argv.length} for 1 or more.")
|
|
18
|
-
end
|
|
17
|
+
def add(*argv)
|
|
18
|
+
abort_if_missing_args(argv, 1, '1 or more')
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
options[:hosts] = '' if options[:hosts].nil?
|
|
23
|
-
hosts = options[:hosts].downcase.split(',').uniq
|
|
20
|
+
names = normalize_names(argv)
|
|
21
|
+
hosts = csv_option_names(options[:hosts])
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
abort_if_automatic_group(
|
|
24
|
+
names,
|
|
25
|
+
"ERROR: Cannot manually manipulate the automatic group 'ungrouped'\n"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
result = Moose::Inventory::Operations::AddGroups
|
|
29
|
+
.new(context: Moose::Inventory::InventoryContext.new(db: db))
|
|
30
|
+
.call(names: names, hosts: hosts)
|
|
31
|
+
render_add_groups_events(result.events)
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
db = Moose::Inventory::DB
|
|
32
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
33
|
-
|
|
34
|
-
# Transaction
|
|
35
|
-
warn_count = 0
|
|
36
|
-
db.transaction do # Transaction start
|
|
37
|
-
names.each do |name|
|
|
38
|
-
# Add the group
|
|
39
|
-
puts "Add group '#{name}':"
|
|
40
|
-
group = db.models[:group].find(name: name)
|
|
41
|
-
hosts_ds = nil
|
|
42
|
-
fmt.puts 2, "- create group..."
|
|
43
|
-
if group.nil?
|
|
44
|
-
group = db.models[:group].create(name: name)
|
|
45
|
-
fmt.puts 4, '- OK'
|
|
46
|
-
else
|
|
47
|
-
warn_count += 1
|
|
48
|
-
fmt.warn "Group '#{name}' already exists, skipping creation.\n"
|
|
49
|
-
fmt.puts 4, "- already exists, skipping."
|
|
50
|
-
hosts_ds = group.hosts_dataset
|
|
51
|
-
fmt.puts 4, '- OK'
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Associate with hosts
|
|
55
|
-
hosts.each do |h|
|
|
56
|
-
next if h.nil? || h.empty?
|
|
57
|
-
fmt.puts 2, "- add association {group:#{name} <-> host:#{ h }}..."
|
|
58
|
-
host = db.models[:host].find(name: h)
|
|
59
|
-
if host.nil?
|
|
60
|
-
warn_count += 1
|
|
61
|
-
fmt.warn "Host '#{h}' doesn't exist, but will be created.\n"
|
|
62
|
-
fmt.puts 4, "- host doesn't exist, creating now..."
|
|
63
|
-
host = db.models[:host].create(name: h)
|
|
64
|
-
fmt.puts 6, "- OK"
|
|
65
|
-
end
|
|
66
|
-
if !hosts_ds.nil? && !hosts_ds[name: h].nil?
|
|
67
|
-
warn_count += 1
|
|
68
|
-
fmt.warn "Association {group:#{name} <-> host:#{ h }}"\
|
|
69
|
-
" already exists, skipping creation.\n"
|
|
70
|
-
fmt.puts 4, "- already exists, skipping."
|
|
71
|
-
else
|
|
72
|
-
group.add_host(host)
|
|
73
|
-
end
|
|
74
|
-
fmt.puts 4, '- OK'
|
|
75
|
-
|
|
76
|
-
# Handle the host's automatic 'ungrouped' group
|
|
77
|
-
ungrouped = host.groups_dataset[name: 'ungrouped']
|
|
78
|
-
unless ungrouped.nil?
|
|
79
|
-
fmt.puts 2, "- remove automatic association {group:ungrouped"\
|
|
80
|
-
" <-> host:#{ h }}..."
|
|
81
|
-
host.remove_group( ungrouped ) unless ungrouped.nil?
|
|
82
|
-
fmt.puts 4, '- OK'
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
fmt.puts 2, '- all OK'
|
|
86
|
-
end
|
|
87
|
-
end # Transaction end
|
|
88
|
-
if warn_count == 0
|
|
33
|
+
if result.warning_count.zero?
|
|
89
34
|
puts 'Succeeded'
|
|
90
35
|
else
|
|
91
36
|
puts 'Succeeded, with warnings.'
|
|
92
37
|
end
|
|
93
38
|
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def render_add_groups_events(events)
|
|
43
|
+
events.each { |event| render_add_groups_event(event) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def render_add_groups_event(event)
|
|
47
|
+
payload = event.payload
|
|
48
|
+
|
|
49
|
+
return render_add_groups_event_puts(event.type, payload) if puts_event?(event.type)
|
|
50
|
+
return render_add_groups_event_warn(event.type, payload) if warn_event?(event.type)
|
|
51
|
+
|
|
52
|
+
render_add_groups_event_fmt(event.type, payload)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def puts_event?(type)
|
|
56
|
+
type == :group_started
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def warn_event?(type)
|
|
60
|
+
%i[group_exists host_missing_created association_exists].include?(type)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_add_groups_event_puts(type, payload)
|
|
64
|
+
puts "Add group '#{payload[:name]}':" if type == :group_started
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def render_add_groups_event_warn(type, payload)
|
|
68
|
+
case type
|
|
69
|
+
when :group_exists
|
|
70
|
+
fmt.warn "Group '#{payload[:name]}' already exists, skipping creation.\n"
|
|
71
|
+
when :host_missing_created
|
|
72
|
+
fmt.warn "Host '#{payload[:name]}' doesn't exist, but will be created.\n"
|
|
73
|
+
when :association_exists
|
|
74
|
+
fmt.warn(
|
|
75
|
+
"Association {group:#{payload[:group]} <-> host:#{payload[:host]}} " \
|
|
76
|
+
"already exists, skipping creation.\n"
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def render_add_groups_event_fmt(type, payload)
|
|
82
|
+
return render_add_groups_event_status(type, payload) if status_event?(type)
|
|
83
|
+
|
|
84
|
+
case type
|
|
85
|
+
when :creating_group
|
|
86
|
+
fmt.puts 2, '- create group...'
|
|
87
|
+
when :adding_association
|
|
88
|
+
fmt.puts 2, "- add association {group:#{payload[:group]} <-> host:#{payload[:host]}}..."
|
|
89
|
+
when :host_creating_now
|
|
90
|
+
fmt.puts 4, '- host doesn\'t exist, creating now...'
|
|
91
|
+
when :removing_automatic_group
|
|
92
|
+
fmt.puts 2, "- remove automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
93
|
+
when :group_complete
|
|
94
|
+
fmt.puts 2, '- all OK'
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def status_event?(type)
|
|
99
|
+
%i[already_exists_skipping ok].include?(type)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def render_add_groups_event_status(type, payload)
|
|
103
|
+
if type == :already_exists_skipping
|
|
104
|
+
fmt.puts payload[:indent], '- already exists, skipping.'
|
|
105
|
+
else
|
|
106
|
+
fmt.puts payload[:indent], '- OK'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
94
109
|
end
|
|
95
110
|
end
|
|
96
111
|
end
|
|
97
112
|
end
|
|
98
|
-
|