vaultkit 1.0.7 → 1.0.8

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: cba4a6b29805455820a7137e864da2d9637bd59e2f4be377bd51875836ee6d5f
4
- data.tar.gz: 33672d087e2cf391855bcd839162b706fab6908daebbf45504d67f7ed4093ff3
3
+ metadata.gz: 3cc5f978b053f8fe4ac4e146a50b90e2fecc93b884073816c760bb0d99502b96
4
+ data.tar.gz: 26b63ecee6b659f0bf0f645455963f712aac27df77850e142dabc894d5b8070a
5
5
  SHA512:
6
- metadata.gz: 3e13e955409aa362de4bb76a526698c0157378dc77c803e80cd0d5c7d5bab39296d4dde4e04b55592c2e1f1ba6d4b20de9ad847263536c69baea791d643a6c1d
7
- data.tar.gz: b355ce713edba89b7d293499489c0b0f4aaae614ca5f3af0249e31a043d1efbb25aa4359f710b86d9dca585bbe7b4b1f7651ac9d2e454197323a6a7bae347b61
6
+ metadata.gz: ba3984df812852cab48f7a530d77d021a8ecbb43432cd212f3ed7d0cd14c71705fce6894f05d3b7db84569168cd4004609fe9f9a702ca2b90c6130c59dcf3402
7
+ data.tar.gz: 7d5c1ca12d0fb08798c6c85bfa2fc10fd6db0667129f7d2fa51e72542dae471c734511f3480e1b73ba3c070903985fb5c42bb789905c1dea3aa4e383d3a6b599
@@ -202,7 +202,7 @@ module Vkit
202
202
 
203
203
  desc "bundle", "Compile YAML policies into a JSON policy bundle"
204
204
  option :policies_dir, type: :string, default: "config/policies"
205
- option :registry_dir, type: :string, default: "config"
205
+ option :registry_dir, type: :string, default: "datasets"
206
206
  option :out, type: :string, default: "dist/policy_bundle.json"
207
207
  option :org, type: :string
208
208
  option :version, type: :string
@@ -16,8 +16,7 @@ module Vkit
16
16
  body: {}
17
17
  )
18
18
 
19
- rows = response.dig("rows", "rows") || []
20
- meta = response.dig("rows", "meta") || {}
19
+ rows, meta = normalize_response(response)
21
20
 
22
21
  print_result(rows, meta, format)
23
22
  end
@@ -42,6 +41,24 @@ module Vkit
42
41
  raise "Unknown format: #{format}"
43
42
  end
44
43
  end
44
+
45
+ def normalize_response(response)
46
+ rows =
47
+ if response["rows"].is_a?(Array)
48
+ response["rows"]
49
+ else
50
+ response.dig("rows", "rows") || []
51
+ end
52
+
53
+ meta =
54
+ if response["meta"].is_a?(Hash)
55
+ response["meta"]
56
+ else
57
+ response.dig("rows", "meta") || {}
58
+ end
59
+
60
+ [rows, meta]
61
+ end
45
62
  end
46
63
  end
47
64
  end
@@ -67,22 +67,21 @@ module Vkit
67
67
  private
68
68
 
69
69
  def git_sha
70
- `git rev-parse HEAD`.strip
71
- rescue
70
+ out = `git rev-parse HEAD 2>/dev/null`.strip
71
+ return out unless out.empty?
72
+
72
73
  Time.now.to_i.to_s
73
74
  end
74
-
75
+
75
76
  def git_repo
76
- `git config --get remote.origin.url`.strip
77
- rescue
78
- nil
77
+ out = `git config --get remote.origin.url 2>/dev/null`.strip
78
+ out.empty? ? nil : out
79
79
  end
80
-
80
+
81
81
  def git_ref
82
- `git rev-parse --abbrev-ref HEAD`.strip
83
- rescue
84
- nil
85
- end
82
+ out = `git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
83
+ out.empty? ? nil : out
84
+ end
86
85
  end
87
86
  end
88
87
  end
@@ -80,11 +80,20 @@ module Vkit
80
80
  exit 0
81
81
 
82
82
  when "ok"
83
- rows = result["rows"] || []
83
+ rows = result["rows"]
84
+ meta = result["meta"] || {}
85
+
86
+ unless rows.is_a?(Array)
87
+ raise "Invalid response: expected rows to be an array"
88
+ end
84
89
 
85
90
  puts "✅ OK — #{rows.size} rows"
86
- puts "ℹ️ Query Metadata:"
87
- puts JSON.pretty_generate(result["meta"] || {})
91
+
92
+ if meta.any?
93
+ puts "ℹ️ Query Metadata:"
94
+ puts JSON.pretty_generate(meta)
95
+ end
96
+
88
97
  puts
89
98
  puts "ℹ️ Data Rows:"
90
99
  Vkit::Core::TableFormatter.render(rows)
@@ -104,7 +104,31 @@ module Vkit
104
104
 
105
105
  def self.extract_masking(p)
106
106
  return unless p.dig("action", "mask")
107
- p["masking"]
107
+
108
+ raw =
109
+ p["masking"] ||
110
+ p.dig("action", "masking") || {}
111
+
112
+ default_method =
113
+ normalize_mask_method(raw["default_method"]) if raw["default_method"]
114
+
115
+ rules =
116
+ case raw["rules"]
117
+ when Hash
118
+ raw["rules"].each_with_object({}) do |(field, method), acc|
119
+ acc[field.to_s] = normalize_mask_method(method)
120
+ end
121
+ else
122
+ {}
123
+ end
124
+
125
+ result = {}
126
+ result["default_method"] = default_method if default_method
127
+ result["rules"] = rules if rules.any?
128
+
129
+ return if result.empty?
130
+
131
+ result
108
132
  end
109
133
 
110
134
  def self.normalize_registry(raw)
@@ -139,6 +163,16 @@ module Vkit
139
163
  end
140
164
  end
141
165
 
166
+ def self.normalize_mask_method(method)
167
+ case method.to_s
168
+ when "redact" then "full"
169
+ when "hash" then "hash"
170
+ when "truncate" then "partial"
171
+ when "nullify" then "full"
172
+ else "full"
173
+ end
174
+ end
175
+
142
176
  # Canonicalization
143
177
  def self.canonical_json(obj)
144
178
  JSON.generate(sort_keys_deep(obj))
@@ -13,3 +13,6 @@ action:
13
13
  mask: true
14
14
  reason: "Agents should not receive raw sensitive fields by default."
15
15
  ttl: "4h"
16
+
17
+ masking:
18
+ default_method: redact
@@ -11,4 +11,7 @@ context: {}
11
11
  action:
12
12
  mask: true
13
13
  reason: "Payment tokens/card-related data must never be exposed in raw form."
14
- ttl: "24h"
14
+ ttl: 24h
15
+
16
+ masking:
17
+ default_method: redact
@@ -1,13 +1,16 @@
1
1
  id: mask_pii_by_default
2
2
  description: "Mask PII fields by default unless explicitly allowed."
3
3
 
4
+ priority: 80
5
+
4
6
  match:
5
7
  fields:
6
- sensitivity: pii
7
-
8
- priority: 80
8
+ contains: ["pii"]
9
9
 
10
10
  action:
11
11
  mask: true
12
12
  reason: "PII is masked by default for safety."
13
- ttl: "1h"
13
+ ttl: 1h
14
+
15
+ masking:
16
+ default_method: redact
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Vkit
2
4
  module Policy
3
5
  class PolicyValidator
@@ -28,7 +30,7 @@ module Vkit
28
30
  MSG
29
31
  end
30
32
 
31
- validate_action!(action, prefix)
33
+ validate_action!(action, policy, prefix)
32
34
  true
33
35
  end
34
36
 
@@ -41,7 +43,7 @@ module Vkit
41
43
  MSG
42
44
  end
43
45
 
44
- def self.validate_action!(action, prefix)
46
+ def self.validate_action!(action, policy, prefix)
45
47
  intents = ACTION_KEYS.select { |k| action[k] == true }
46
48
 
47
49
  if intents.empty?
@@ -81,7 +83,8 @@ module Vkit
81
83
  require_string!(action, "approver_role", prefix)
82
84
 
83
85
  when "mask"
84
- # masking config can be validated later
86
+ # masking is optional, but if present must be valid
87
+ validate_masking!(policy, prefix) if policy.key?("masking")
85
88
 
86
89
  when "allow"
87
90
  # nothing required
@@ -124,6 +127,73 @@ module Vkit
124
127
  MSG
125
128
  end
126
129
  end
130
+
131
+ # Masking validation (aligned with compiler + runtime)
132
+ def self.validate_masking!(policy, prefix)
133
+ masking = policy["masking"]
134
+ return unless masking
135
+
136
+ unless masking.is_a?(Hash)
137
+ raise ValidationError, "#{prefix}masking must be a mapping."
138
+ end
139
+
140
+ allowed_keys = %w[default_method rules]
141
+ unknown_keys = masking.keys - allowed_keys
142
+
143
+ if unknown_keys.any?
144
+ raise ValidationError, <<~MSG
145
+ #{prefix}Unknown keys in masking: #{unknown_keys.join(', ')}
146
+
147
+ Allowed keys:
148
+ - default_method
149
+ - rules
150
+ MSG
151
+ end
152
+
153
+ if masking["default_method"]
154
+ validate_mask_method!(
155
+ masking["default_method"],
156
+ prefix,
157
+ "masking.default_method"
158
+ )
159
+ end
160
+
161
+ if masking["rules"]
162
+ unless masking["rules"].is_a?(Hash)
163
+ raise ValidationError,
164
+ "#{prefix}masking.rules must be a mapping of field → method."
165
+ end
166
+
167
+ masking["rules"].each do |field, method|
168
+ validate_mask_method!(
169
+ method,
170
+ prefix,
171
+ "masking.rules.#{field}"
172
+ )
173
+ end
174
+ end
175
+ end
176
+
177
+ def self.validate_mask_method!(method, prefix, path)
178
+ allowed = %w[redact hash truncate nullify full partial]
179
+
180
+ unless allowed.include?(method.to_s)
181
+ raise ValidationError, <<~MSG
182
+ #{prefix}Invalid masking method at #{path}.
183
+
184
+ Allowed values:
185
+ - redact
186
+ - hash
187
+ - truncate
188
+ - nullify
189
+ - full
190
+ - partial
191
+
192
+ Got:
193
+ #{method.inspect}
194
+ MSG
195
+ end
196
+ end
127
197
  end
128
198
  end
129
199
  end
@@ -264,19 +264,15 @@
264
264
  "type": "object",
265
265
  "additionalProperties": false,
266
266
  "properties": {
267
- "fields": {
268
- "type": "array",
269
- "items": {
270
- "type": "object",
271
- "required": ["name", "method"],
272
- "additionalProperties": false,
273
- "properties": {
274
- "name": { "type": "string" },
275
- "method": {
276
- "type": "string",
277
- "enum": ["redact", "hash", "truncate", "nullify"]
278
- }
279
- }
267
+ "default_method": {
268
+ "type": "string",
269
+ "enum": ["full", "hash", "partial"]
270
+ },
271
+ "rules": {
272
+ "type": "object",
273
+ "additionalProperties": {
274
+ "type": "string",
275
+ "enum": ["full", "hash", "partial"]
280
276
  }
281
277
  }
282
278
  }
data/lib/vkit/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vkit
4
- VERSION = "1.0.7"
4
+ VERSION = "1.0.8"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vaultkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nnamdi Ogundu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-17 00:00:00.000000000 Z
11
+ date: 2026-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor