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 +4 -4
- data/lib/vkit/cli/base_cli.rb +1 -1
- data/lib/vkit/cli/commands/fetch_command.rb +19 -2
- data/lib/vkit/cli/commands/policy_bundle_command.rb +10 -11
- data/lib/vkit/cli/commands/request_command.rb +12 -3
- data/lib/vkit/policy/bundle_compiler.rb +35 -1
- data/lib/vkit/policy/packs/ai_safety/policies/03_mask_sensitive_by_default_for_agents.yaml +3 -0
- data/lib/vkit/policy/packs/financial_compliance/policies/02_mask_payment_tokens.yaml +4 -1
- data/lib/vkit/policy/packs/starter/policies/02_mask_pii_by_default.yaml +7 -4
- data/lib/vkit/policy/policy_validator.rb +73 -3
- data/lib/vkit/policy/schema/policy_bundle.schema.json +9 -13
- data/lib/vkit/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3cc5f978b053f8fe4ac4e146a50b90e2fecc93b884073816c760bb0d99502b96
|
|
4
|
+
data.tar.gz: 26b63ecee6b659f0bf0f645455963f712aac27df77850e142dabc894d5b8070a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba3984df812852cab48f7a530d77d021a8ecbb43432cd212f3ed7d0cd14c71705fce6894f05d3b7db84569168cd4004609fe9f9a702ca2b90c6130c59dcf3402
|
|
7
|
+
data.tar.gz: 7d5c1ca12d0fb08798c6c85bfa2fc10fd6db0667129f7d2fa51e72542dae471c734511f3480e1b73ba3c070903985fb5c42bb789905c1dea3aa4e383d3a6b599
|
data/lib/vkit/cli/base_cli.rb
CHANGED
|
@@ -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: "
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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))
|
|
@@ -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
|
-
|
|
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:
|
|
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
|
|
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
|
-
"
|
|
268
|
-
"type": "
|
|
269
|
-
"
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
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.
|
|
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-
|
|
11
|
+
date: 2026-02-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|