localvault 1.9.1 → 1.9.2
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/README.md +3 -3
- data/lib/localvault/cli.rb +3 -3
- data/lib/localvault/guard.rb +45 -1
- data/lib/localvault/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f52091ddaad3306c188919a18acc4cae01aff5d0582e3ca07426b6ab3dede07e
|
|
4
|
+
data.tar.gz: 4db7f05666bbb3644b436561e82bcbd51b2b8402618885469ec0534abed60302
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 76a6c6007b74d7287673345f0e11b08cc4dca663381b04e6e23e1917d84c92a45f0f9f809be73d028492617b905a66968b0c98791ab0a2772980b94f0e9bcf58
|
|
7
|
+
data.tar.gz: 10751a27f1d8bdce859045e678af61a968a18fe65679dc015a60056502ede9fb4de414439f234ca550da3721f0bd63aaa3e3b93895676b653036befd29d0a348
|
data/README.md
CHANGED
|
@@ -88,11 +88,11 @@ localvault exec -- rails server
|
|
|
88
88
|
| `init [NAME]` | Create a vault (Argon2id key derivation) |
|
|
89
89
|
| `set KEY --stdin` | Store a secret from stdin without argv/history exposure |
|
|
90
90
|
| `set KEY [VALUE]` | Store a secret (positional value kept for compatibility) |
|
|
91
|
-
| `set --group GROUP KEY --stdin` | Store a secret in a named group from stdin |
|
|
91
|
+
| `set --group GROUP KEY --stdin` | Store a secret in a named group from stdin (`-g`) |
|
|
92
92
|
| `get KEY` | Retrieve a secret (raw, pipeable) |
|
|
93
93
|
| `show` | Display all secrets in a table (masked by default) |
|
|
94
|
-
| `show --reveal` | Display with values visible |
|
|
95
|
-
| `show --group` | Group by dot-notation prefix
|
|
94
|
+
| `show --reveal` | Display with values visible (`-r`) |
|
|
95
|
+
| `show --group` | Group by dot-notation prefix, one table per project (`-g`) |
|
|
96
96
|
| `show --group QUERY` | Show one exact or uniquely matching group |
|
|
97
97
|
| `groups [QUERY]` | List/search group names and key counts without values |
|
|
98
98
|
| `list` | List key names only |
|
data/lib/localvault/cli.rb
CHANGED
|
@@ -243,7 +243,7 @@ module LocalVault
|
|
|
243
243
|
Use `localvault show -p platepose -v vault` to view a single project.
|
|
244
244
|
Use `localvault import` to bulk-load from a .env, .json, or .yml file.
|
|
245
245
|
DESC
|
|
246
|
-
method_option :group, type: :string, desc: "Store KEY and VALUE inside this named group"
|
|
246
|
+
method_option :group, aliases: "-g", type: :string, desc: "Store KEY and VALUE inside this named group"
|
|
247
247
|
method_option :stdin, type: :boolean, default: false, desc: "Read VALUE from stdin instead of argv"
|
|
248
248
|
def set(key, value = nil)
|
|
249
249
|
validate_secret_value_source!(value)
|
|
@@ -526,8 +526,8 @@ module LocalVault
|
|
|
526
526
|
\x05 localvault show -p platepose -v intellectaco # one project only
|
|
527
527
|
\x05 localvault show -p platepose -v intellectaco --reveal
|
|
528
528
|
DESC
|
|
529
|
-
method_option :group, type: :string, lazy_default: GROUP_ALL_SENTINEL, desc: "Show all groups or one group by name"
|
|
530
|
-
method_option :reveal,
|
|
529
|
+
method_option :group, aliases: "-g", type: :string, lazy_default: GROUP_ALL_SENTINEL, desc: "Show all groups or one group by name"
|
|
530
|
+
method_option :reveal, aliases: "-r", type: :boolean, default: false, desc: "Show full values instead of masking"
|
|
531
531
|
method_option :project, aliases: "-p", type: :string, desc: "Show only this project group"
|
|
532
532
|
def show
|
|
533
533
|
vault = open_vault!
|
data/lib/localvault/guard.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "json"
|
|
2
|
+
require "yaml"
|
|
2
3
|
require "digest"
|
|
3
4
|
require_relative "store"
|
|
4
5
|
require_relative "session_cache"
|
|
@@ -15,6 +16,44 @@ module LocalVault
|
|
|
15
16
|
# check gets uninstalled.
|
|
16
17
|
module Guard
|
|
17
18
|
MIN_VALUE_LENGTH = 8
|
|
19
|
+
|
|
20
|
+
# Keys the operator has explicitly declared non-secret, from
|
|
21
|
+
# ~/.localvault/config.yml:
|
|
22
|
+
#
|
|
23
|
+
# guard_ignore:
|
|
24
|
+
# - CLOUDFLARE_ASSETS.r2_bucket
|
|
25
|
+
#
|
|
26
|
+
# WHY EXPLICIT, AND NOT INFERRED FROM THE KEY NAME.
|
|
27
|
+
# The first attempt exempted keys whose name *looked* public — anything
|
|
28
|
+
# ending in bucket / region / _id — with a veto list of credential-ish
|
|
29
|
+
# words. An audit killed it in one line: a value stored under `AWS.bucket`
|
|
30
|
+
# would be exempt regardless of what it actually contained, so the fix
|
|
31
|
+
# traded a false positive for a false NEGATIVE in a security tool. The veto
|
|
32
|
+
# list was also unbounded (pem, jwk, seed, mnemonic, salt, otp, bearer,
|
|
33
|
+
# sas, …) and every omission is a silent bypass.
|
|
34
|
+
#
|
|
35
|
+
# A key's name cannot describe its value. Only the operator can say "this
|
|
36
|
+
# one is public", so only the operator may — by naming the exact full key,
|
|
37
|
+
# in a file they control, which is greppable and auditable.
|
|
38
|
+
#
|
|
39
|
+
# The problem being solved is still real: a public identifier stored beside
|
|
40
|
+
# real secrets (a bucket name that happens to be a substring of a project
|
|
41
|
+
# path) denies every command mentioning that path, and a guard that cries
|
|
42
|
+
# wolf gets uninstalled, which protects nothing. The answer is an explicit
|
|
43
|
+
# allowlist, not a clever one.
|
|
44
|
+
def self.ignored_keys(config_path = default_config_path)
|
|
45
|
+
return [] unless File.exist?(config_path)
|
|
46
|
+
|
|
47
|
+
raw = YAML.safe_load(File.read(config_path)) || {}
|
|
48
|
+
Array(raw["guard_ignore"]).map(&:to_s)
|
|
49
|
+
rescue StandardError
|
|
50
|
+
[] # an unreadable config must never widen the exemption
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.default_config_path
|
|
54
|
+
File.join(Dir.home, ".localvault", "config.yml")
|
|
55
|
+
end
|
|
56
|
+
|
|
18
57
|
HOOK_ENTRYPOINT = "localvault guard hook".freeze
|
|
19
58
|
# The installed command must fail open on machines where the binary is
|
|
20
59
|
# old, missing, or broken — otherwise every Bash call errors for users
|
|
@@ -55,12 +94,17 @@ module LocalVault
|
|
|
55
94
|
end
|
|
56
95
|
|
|
57
96
|
# @return [Array<Match>] stored secret values appearing in +text+
|
|
58
|
-
def self.scan(text, secrets = unlocked_secrets)
|
|
97
|
+
def self.scan(text, secrets = unlocked_secrets, ignore: ignored_keys)
|
|
59
98
|
return [] if text.nil? || text.empty?
|
|
60
99
|
|
|
100
|
+
ignored = Array(ignore).map(&:to_s)
|
|
101
|
+
|
|
61
102
|
secrets.filter_map do |entry|
|
|
62
103
|
value = entry[:value]
|
|
63
104
|
next if value.nil? || value.length < MIN_VALUE_LENGTH
|
|
105
|
+
# Exact full-key match only. No prefixes, no suffix inference — an
|
|
106
|
+
# operator opting one key out must never silently opt out its siblings.
|
|
107
|
+
next if ignored.include?(entry[:key].to_s)
|
|
64
108
|
next unless text.include?(value)
|
|
65
109
|
|
|
66
110
|
Match.new(vault: entry[:vault], key: entry[:key], fingerprint: fingerprint(value))
|
data/lib/localvault/version.rb
CHANGED