baseline-rb 0.3.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a69b4397f4b697acf294fb60e48af3ab3971ab7f17802d1bbaff4b87c6bc265
4
- data.tar.gz: 965cffbfdf50b85f3c5ad210d7c21f58dedae938c156bc6e673ea468a15085f7
3
+ metadata.gz: 0ab48a464a299fae539f474ad2fdfce3079f8b6b49f930b72e92d3e686b0fd11
4
+ data.tar.gz: 500bf4fde6b208357f3fd8aa1fcbb8df4f24ed046568b9ed8ed888a2ee566586
5
5
  SHA512:
6
- metadata.gz: 3c1492e3a89e7ae5fe118439a46002f3fa2750973f95c11368844bf044efd91bebfc24fd6c81e67441f4a12abbeaf6e0e22721e366090499e36e0ed75bea4042
7
- data.tar.gz: f362962731008df44b80e3116fb8a0e55019d2aa775f2a2560f0255345775848dbabe737bf8c46d50903fc7afa9ac09e5a008a5f722eb5e700ea02c40caf9ba4
6
+ metadata.gz: 503da5d853fd3d86c8503f2df927fd9da752b8a4416552855e3063f0e5ee902610682de09eb023f5c7a645754aac552e1e6c6b947a646ea22f43cd567de6daec
7
+ data.tar.gz: dbfdd6e5590c2d2eb29c375858edaba14ff599452059f5ad0011b592e95c09c35c95826a021b5787e129e8d3f2d91f32bd8e7d775ec46cddbab57f549f28c2c4
data/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  Changes to the public contract are recorded here. Versions follow Semantic Versioning.
4
4
 
5
+ ## 0.3.1 (2026-10-18)
6
+
7
+ - Withhold IOS SNMP ACL facts after contradictory community access declarations, while accepting equivalent repeats and preserving NX-OS incremental attributes.
8
+
9
+ - Clear Bundler bootstrap variables when verifying packages in an isolated gem installation, including calls made through bundle exec.
10
+
11
+ - Remove cancelled IOS SNMP communities and their credential evidence before deriving access and permission facts.
12
+
13
+ - Keep management restrictions unknown when vendor ACL rules repeat an explicit sequence identity.
14
+ - Stop reusing Cisco TACACS login methods after the final named list is cancelled or malformed; independent lists remain available.
15
+
5
16
  ## 0.3.0 (2026-10-18)
6
17
 
7
18
  - Breaking: rename mutable fact assembly to `FactBuilder`, immutable parsed snapshots to `Evidence`, multiline quoted-value documents to `MultilineDocument`, and the PAN-OS prefix lookup to `CommandIndex#matching_prefix`.
@@ -147,8 +147,8 @@ module Baseline
147
147
  enabled = nxos? ? configured_state(/\Asnmp-server protocol enable\z/, /\Ano snmp-server protocol enable\z/) : nil
148
148
  enabled = nxos? || snmp_entries.any? if enabled.nil? && (@fact_builder.complete? || snmp_entries.any?)
149
149
  record_fact("snmp.enabled", enabled)
150
- communities = {}
151
- matching_blocks(/\Asnmp-server community\b/).each do |entry|
150
+ communities, community_acls = {}, {}
151
+ configured_community_entries.each do |entry|
152
152
  match = /\Asnmp-server community\s+(\S+)(?:\s+(.*))?\z/.match(entry.text)
153
153
  unless match
154
154
  record_unknown_snmp_access([ entry ])
@@ -162,6 +162,14 @@ module Baseline
162
162
  community[:write] = SNMP_ROLE_WRITE[role] if role
163
163
  acl = nxos? ? settings[/\buse-ipv4acl\s+(\S+)/, 1] : settings[/\A(?:ro|rw)\s+(\S+)\z/i, 1]
164
164
  community[:acl] = acl if acl
165
+ unless nxos?
166
+ access = (community_acls[match[1]] ||= { value: acl, conflicted: false })
167
+ access[:conflicted] ||= access[:value] != acl
168
+ if access[:conflicted]
169
+ community.delete(:acl)
170
+ record_unknown_fact("snmp.acl", [ entry ], code: :conflicting_community_acl)
171
+ end
172
+ end
165
173
  record_credential("snmp-community", "snmp", "plain", match[1], entry) unless @fact_builder.credential_recorded?(community[:line])
166
174
  end
167
175
  versions = communities.any? ? %w[v1 v2c] : []
@@ -226,6 +234,24 @@ module Baseline
226
234
  record_fact("snmp.versions", versions) unless versions.empty?
227
235
  end
228
236
 
237
+ # IOS 的 no 声明撤销整项团体字;先归约有效来源,避免留下访问或凭据事实。
238
+ def configured_community_entries
239
+ return matching_blocks(/\Asnmp-server community\b/) if nxos?
240
+
241
+ active = {}
242
+ matching_blocks(/\A(?:no )?snmp-server community\b/).each do |entry|
243
+ if (match = /\Ano snmp-server community\s+(\S+)\z/.match(entry.text))
244
+ analyze(entry) { active.delete(match[1]) }
245
+ elsif entry.text.start_with?("no ")
246
+ record_unknown_snmp_access([ entry ])
247
+ else
248
+ name = entry.text.split[2]
249
+ (active[name] ||= []) << entry
250
+ end
251
+ end
252
+ active.values.flatten.sort_by(&:line)
253
+ end
254
+
229
255
  # NX-OS 通常会同步 CLI 与 SNMP 账号;缺少证据时不能构造虚拟用户。
230
256
  def record_unverified_snmp_user_sync(users, enabled:)
231
257
  return if enabled == false
@@ -318,8 +344,12 @@ module Baseline
318
344
  # 解析各 VTY 实际选用的登录列表,包括命名列表。
319
345
  def record_tacacs_login_usage(servers, groups)
320
346
  pattern = /\Aaaa authentication login\s+(\S+)\s+(.+)\z/
321
- lists = matching_blocks(pattern).each_with_object({}) do |entry, result|
322
- name, methods = analyze(entry) { entry.text.match(pattern).captures }
347
+ family = /\A(?:no )?aaa authentication login\s+(\S+)(?:\s|\z)/
348
+ lists = matching_blocks(family).each_with_object({}) do |entry, result|
349
+ name = entry.text[family, 1]
350
+ match = pattern.match(entry.text)
351
+ # 最后同名声明决定列表证据;撤销或损坏不能复用先前的方法。
352
+ methods = match ? analyze(entry) { match[2] } : nil
323
353
  result[name] = { methods: methods, entry: entry }
324
354
  end
325
355
  entries = @fact_builder.vtys.filter_map { |vty| lists[vty[:authentication_list]]&.fetch(:entry) }.uniq
@@ -329,7 +359,7 @@ module Baseline
329
359
  elsif vty[:authentication] != "aaa"
330
360
  false
331
361
  elsif (list = lists[vty[:authentication_list]])
332
- tacacs_login_method_state(list[:methods], servers, groups)
362
+ list[:methods] ? tacacs_login_method_state(list[:methods], servers, groups) : :authentication_list_unresolved
333
363
  else
334
364
  :authentication_list_unresolved
335
365
  end
@@ -244,6 +244,12 @@ module Baseline
244
244
  # 注册有序 ACL 规则与别名,同时保留不支持的语法。
245
245
  def register_acl(name, entries, aliases: [], supported: true, source: nil, &block)
246
246
  commands = entries.reject { |entry| /\A(?:\d+\s+)?(?:description|remark|step|statistics|hardware-count)\b/.match?(entry.text) }
247
+ # 厂商显式序号是规则身份;重复身份不能靠来源先后消解。无序号规则保留原有顺序语义。
248
+ sequences = commands.filter_map do |entry|
249
+ match = /\A(?:access-list\s+\S+\s+)?(?:rule\s+)?(\d+)\s+(?:permit|deny)\b/.match(entry.text)
250
+ Integer(match[1], 10) if match
251
+ end
252
+ duplicate_sequences = sequences.uniq.size != sequences.size
247
253
  parsed = commands.map.with_index do |entry, index|
248
254
  value = analyze(entry) { block.call(entry, index) }
249
255
  cursor.mark(entry.index, state: :unsupported) unless value
@@ -256,8 +262,9 @@ module Baseline
256
262
  source ? source.line_range.cover?(line) : damaged_numbered_acl?(cursor.lines.fetch(line - 1).text, name)
257
263
  end
258
264
  acl = Acl.new(name: name, line: entries.first&.line, rules: parsed.compact,
259
- supported: supported && !damaged && parsed.none?(&:nil?))
260
- if @fact_builder.register_acl(acl, aliases: aliases, lines: entries.map(&:line)).any?
265
+ supported: supported && !damaged && !duplicate_sequences && parsed.none?(&:nil?))
266
+ conflicts = @fact_builder.register_acl(acl, aliases: aliases, lines: entries.map(&:line))
267
+ if duplicate_sequences || conflicts.any?
261
268
  entries.each { |entry| cursor.mark(entry.index, state: :unsupported) }
262
269
  end
263
270
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Baseline
4
4
  # 公开包版本,供 RubyGems 元数据和运行时调用方共用。
5
- VERSION = "0.3.0"
5
+ VERSION = "0.3.1"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: baseline-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gatework contributors