secure-keys 1.1.6 → 1.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 +4 -4
- data/README.md +360 -148
- data/lib/core/console/arguments/fetchable.rb +38 -0
- data/lib/core/console/arguments/handler.rb +7 -23
- data/lib/core/console/arguments/parser.rb +28 -2
- data/lib/core/console/arguments/xcframework/parser.rb +1 -1
- data/lib/core/console/shell.rb +1 -3
- data/lib/core/environment/ci.rb +15 -2
- data/lib/core/generator.rb +9 -9
- data/lib/core/utils/extensions/kernel.rb +2 -2
- data/lib/core/utils/swift/package.rb +1 -1
- data/lib/core/utils/swift/writer.rb +2 -4
- data/lib/core/utils/swift/xcframework.rb +1 -1
- data/lib/keys.rb +6 -6
- data/lib/services/environment.rb +38 -0
- data/lib/validation/actions/scan.rb +126 -0
- data/lib/validation/console/arguments/parser.rb +65 -0
- data/lib/validation/console/arguments/scan/handler.rb +31 -0
- data/lib/validation/console/arguments/scan/parser.rb +61 -0
- data/lib/validation/globals/globals.rb +71 -0
- data/lib/validation/models/finding.rb +76 -0
- data/lib/validation/models/scan_result.rb +47 -0
- data/lib/validation/scanner.rb +269 -0
- data/lib/validation/utils/entropy.rb +24 -0
- data/lib/validation/utils/min_length.rb +16 -0
- data/lib/validation/utils/patterns.rb +204 -0
- data/lib/validation/utils/weak_secrets.rb +13 -0
- data/lib/validation/validation_issue.rb +55 -0
- data/lib/validation/validation_result.rb +117 -0
- data/lib/validation/validator.rb +203 -0
- data/lib/version.rb +2 -1
- metadata +21 -58
data/README.md
CHANGED
|
@@ -1,140 +1,168 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="https://img.shields.io/badge/version-1.1.6-cyan" alt="SecureKeys version">
|
|
1
|
+
# Secure Key Generator for iOS Projects
|
|
3
2
|
|
|
3
|
+
<div style="display: flex; gap: 10px; padding-bottom: 20px;">
|
|
4
|
+
<img src="https://img.shields.io/badge/version-1.2.0-cyan" alt="SecureKeys version">
|
|
4
5
|
<img src="https://img.shields.io/badge/iOS-^13.0-blue" alt="iOS version 13.0">
|
|
5
|
-
|
|
6
6
|
<img src="https://img.shields.io/badge/Ruby-^3.3.6-red" alt="Ruby version 3.3.6">
|
|
7
|
-
|
|
8
7
|
</div>
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Utility to generate a `xcframework` for handling secure keys in iOS projects.
|
|
9
|
+
`secure-keys` is a Ruby CLI that generates a `SecureKeys.xcframework` for iOS apps. It reads secret values from macOS Keychain on local machines or environment variables in CI, encrypts those values with AES-256-GCM, writes a Swift API, builds an XCFramework, and optionally adds that framework to an Xcode target.
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
## Requirements
|
|
15
12
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
13
|
+
- macOS 11.0 or later
|
|
14
|
+
- Ruby 3.3 or later
|
|
15
|
+
- Xcode command line tools
|
|
16
|
+
- iOS 13.0 or later
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
## Installation
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
Install with Homebrew:
|
|
23
21
|
|
|
24
22
|
```bash
|
|
25
23
|
brew tap derian-cordoba/secure-keys
|
|
26
|
-
|
|
27
24
|
brew install derian-cordoba/secure-keys/secure-keys
|
|
28
25
|
```
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Another way, you can install the `SecureKeys` utility using `gem` command:
|
|
27
|
+
Install with RubyGems:
|
|
33
28
|
|
|
34
29
|
```bash
|
|
35
30
|
gem install secure-keys
|
|
36
31
|
```
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
Install with Bundler:
|
|
39
34
|
|
|
40
35
|
```ruby
|
|
41
36
|
gem 'secure-keys'
|
|
42
37
|
```
|
|
43
38
|
|
|
44
|
-
Then, you can install the gem using:
|
|
45
|
-
|
|
46
39
|
```bash
|
|
47
40
|
bundle install
|
|
48
41
|
```
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
From an iOS project root:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
security add-generic-password -a "secure-keys" -s "secure-keys" -w "apiKey,githubToken"
|
|
49
|
+
security add-generic-password -a "secure-keys" -s "apiKey" -w "your-api-key"
|
|
50
|
+
security add-generic-password -a "secure-keys" -s "githubToken" -w "your-github-token"
|
|
51
|
+
|
|
52
|
+
secure-keys
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The command creates `.secure-keys/SecureKeys.xcframework`.
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
Use the generated framework from Swift:
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
```swift
|
|
60
|
+
import SecureKeys
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
let apiKey = SecureKey.apiKey.decryptedValue
|
|
63
|
+
let githubToken = key(for: .githubToken)
|
|
64
|
+
```
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
## How Secret Generation Works
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
`secure-keys` does not create third-party API keys for providers such as GitHub, Firebase, Stripe, or AWS. Those secrets must be created in their owning service first.
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
The tool generates an iOS framework that contains encrypted copies of the secret values you provide:
|
|
63
71
|
|
|
64
|
-
1.
|
|
72
|
+
1. Resolve the secret source:
|
|
73
|
+
- Local runs use macOS Keychain unless CI mode is enabled.
|
|
74
|
+
- CI runs use environment variables.
|
|
75
|
+
2. Read the configured list of secret names.
|
|
76
|
+
3. Read each secret value by name.
|
|
77
|
+
4. Generate a random AES-256-GCM key for this build.
|
|
78
|
+
5. Encrypt each secret value.
|
|
79
|
+
6. Write a Swift `SecureKey` enum with encrypted byte arrays.
|
|
80
|
+
7. Build `.secure-keys/SecureKeys.xcframework`.
|
|
81
|
+
8. Remove temporary Swift package files.
|
|
65
82
|
|
|
66
|
-
|
|
83
|
+
## Local Configuration With Keychain
|
|
84
|
+
|
|
85
|
+
The default Keychain service and account identifier is `secure-keys`.
|
|
86
|
+
|
|
87
|
+
Store the list of secret names:
|
|
67
88
|
|
|
68
89
|
```bash
|
|
69
90
|
security add-generic-password -a "secure-keys" -s "secure-keys" -w "githubToken,apiKey"
|
|
70
91
|
```
|
|
71
92
|
|
|
72
|
-
|
|
93
|
+
Store each secret value under the same Keychain service:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
security add-generic-password -a "secure-keys" -s "githubToken" -w "your-github-token"
|
|
97
|
+
security add-generic-password -a "secure-keys" -s "apiKey" -w "your-api-key"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use a custom Keychain identifier:
|
|
73
101
|
|
|
74
102
|
```bash
|
|
75
|
-
export SECURE_KEYS_IDENTIFIER="
|
|
103
|
+
export SECURE_KEYS_IDENTIFIER="my-app-secrets"
|
|
76
104
|
|
|
77
105
|
security add-generic-password -a "$SECURE_KEYS_IDENTIFIER" -s "$SECURE_KEYS_IDENTIFIER" -w "githubToken,apiKey"
|
|
106
|
+
security add-generic-password -a "$SECURE_KEYS_IDENTIFIER" -s "githubToken" -w "your-github-token"
|
|
107
|
+
security add-generic-password -a "$SECURE_KEYS_IDENTIFIER" -s "apiKey" -w "your-api-key"
|
|
78
108
|
```
|
|
79
109
|
|
|
80
|
-
|
|
110
|
+
Use a custom delimiter:
|
|
81
111
|
|
|
82
112
|
```bash
|
|
83
|
-
|
|
113
|
+
export SECURE_KEYS_DELIMITER="|"
|
|
114
|
+
security add-generic-password -a "secure-keys" -s "secure-keys" -w "githubToken|apiKey"
|
|
84
115
|
```
|
|
85
116
|
|
|
86
|
-
|
|
117
|
+
## CI Configuration With Environment Variables
|
|
118
|
+
|
|
119
|
+
CI mode is enabled automatically when common CI variables are present, including `CI=true` and `GITHUB_ACTIONS=true`. You can also force it:
|
|
87
120
|
|
|
88
121
|
```bash
|
|
89
|
-
|
|
122
|
+
secure-keys --ci
|
|
90
123
|
```
|
|
91
124
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
1. You can define the keys in the `.env` file or export the keys as environment variables.
|
|
125
|
+
Set the list of secret names in `SECURE_KEYS_IDENTIFIER`:
|
|
95
126
|
|
|
96
127
|
```bash
|
|
97
128
|
export SECURE_KEYS_IDENTIFIER="github-token,api_key,firebaseToken"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Set each secret value as an environment variable. Secret names are looked up exactly first, then normalized by converting `-` to `_` and uppercasing.
|
|
98
132
|
|
|
133
|
+
```bash
|
|
99
134
|
export GITHUB_TOKEN="your-github-token"
|
|
100
135
|
export API_KEY="your-api-key"
|
|
101
136
|
export FIREBASETOKEN="your-firebase-token"
|
|
102
137
|
```
|
|
103
138
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
> [!IMPORTANT]
|
|
107
|
-
> If you want to use another demiliter, you can define an env variable named `SECURE_KEYS_DELIMITER` to set the delimiter.
|
|
139
|
+
The `SECURE_KEYS_` prefix is also supported for secret values:
|
|
108
140
|
|
|
109
141
|
```bash
|
|
110
|
-
export
|
|
111
|
-
|
|
112
|
-
export SECURE_KEYS_IDENTIFIER="github-token|api_key|firebaseToken"
|
|
142
|
+
export SECURE_KEYS_API_KEY="your-api-key"
|
|
113
143
|
```
|
|
114
144
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
To generate the `SecureKeys.xcframework` use the `secure-keys` command in the iOS project root directory.
|
|
118
|
-
|
|
119
|
-
Using global gem:
|
|
145
|
+
You can store the list in a dedicated environment variable instead:
|
|
120
146
|
|
|
121
147
|
```bash
|
|
122
|
-
|
|
148
|
+
export CUSTOM_SECRET_LIST="github-token,api_key"
|
|
149
|
+
secure-keys --ci --identifier CUSTOM_SECRET_LIST
|
|
123
150
|
```
|
|
124
151
|
|
|
125
|
-
|
|
152
|
+
Use a custom delimiter in CI:
|
|
126
153
|
|
|
127
154
|
```bash
|
|
128
|
-
|
|
155
|
+
export SECURE_KEYS_DELIMITER="|"
|
|
156
|
+
export SECURE_KEYS_IDENTIFIER="github-token|api_key|firebaseToken"
|
|
129
157
|
```
|
|
130
158
|
|
|
131
|
-
|
|
159
|
+
## CLI Reference
|
|
132
160
|
|
|
133
161
|
```bash
|
|
134
162
|
secure-keys --help
|
|
163
|
+
```
|
|
135
164
|
|
|
136
|
-
|
|
137
|
-
|
|
165
|
+
```text
|
|
138
166
|
Usage: secure-keys [--options]
|
|
139
167
|
|
|
140
168
|
-h, --help Use the provided commands to select the params
|
|
@@ -147,172 +175,356 @@ Usage: secure-keys [--options]
|
|
|
147
175
|
--xcframework Add the xcframework to the target
|
|
148
176
|
```
|
|
149
177
|
|
|
150
|
-
|
|
178
|
+
Examples:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
secure-keys
|
|
182
|
+
secure-keys --verbose
|
|
183
|
+
secure-keys --ci --identifier CUSTOM_SECRET_LIST
|
|
184
|
+
secure-keys --identifier "my-app-secrets" --delimiter "|"
|
|
185
|
+
secure-keys -i "my-app-secrets" -d "|"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Xcode Integration
|
|
189
|
+
|
|
190
|
+
Generate the framework only:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
secure-keys
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Generate and add the framework to an Xcode target:
|
|
151
197
|
|
|
152
198
|
```bash
|
|
153
|
-
secure-keys --
|
|
199
|
+
secure-keys --xcframework --target "YourTargetName" --add
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Replace an existing framework reference:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
secure-keys --xcframework --target "YourTargetName" --replace
|
|
154
206
|
```
|
|
155
207
|
|
|
156
|
-
|
|
208
|
+
Add an already generated framework without rebuilding:
|
|
157
209
|
|
|
158
210
|
```bash
|
|
159
|
-
secure-keys -
|
|
211
|
+
secure-keys --no-generate --xcframework --target "YourTargetName"
|
|
160
212
|
```
|
|
161
213
|
|
|
162
|
-
|
|
214
|
+
Select a project explicitly:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
secure-keys --xcframework --target "YourTargetName" --xcodeproj "/path/to/YourProject.xcodeproj"
|
|
218
|
+
```
|
|
163
219
|
|
|
164
|
-
|
|
220
|
+
The same options can be configured with environment variables:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
export SECURE_KEYS_XCFRAMEWORK_TARGET="YourTargetName"
|
|
224
|
+
export SECURE_KEYS_XCFRAMEWORK_ADD=true
|
|
225
|
+
export SECURE_KEYS_XCFRAMEWORK_REPLACE=false
|
|
226
|
+
export SECURE_KEYS_XCFRAMEWORK_XCODEPROJ="/path/to/YourProject.xcodeproj"
|
|
227
|
+
|
|
228
|
+
secure-keys --xcframework
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Short environment variable names are also supported:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
export XCFRAMEWORK_TARGET="YourTargetName"
|
|
235
|
+
export XCFRAMEWORK_ADD=true
|
|
236
|
+
export XCFRAMEWORK_REPLACE=false
|
|
237
|
+
export XCFRAMEWORK_XCODEPROJ="/path/to/YourProject.xcodeproj"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Manual Xcode Setup
|
|
241
|
+
|
|
242
|
+
If you do not use `--xcframework`, add the framework manually:
|
|
243
|
+
|
|
244
|
+
1. Open the Xcode project target.
|
|
245
|
+
2. Open `General`.
|
|
246
|
+
3. Add `.secure-keys/SecureKeys.xcframework` to `Frameworks, Libraries, and Embedded Content`.
|
|
247
|
+
4. Open `Build Settings`.
|
|
248
|
+
5. Add `$(SRCROOT)/.secure-keys` to `Framework Search Paths`.
|
|
249
|
+
|
|
250
|
+
## Swift API
|
|
251
|
+
|
|
252
|
+
The generated framework exposes `SecureKey`, `key(for:)`, `key(_:)`, and a `String.secretKey` helper.
|
|
165
253
|
|
|
166
254
|
```swift
|
|
167
255
|
import SecureKeys
|
|
168
256
|
|
|
169
|
-
// Using key directly in the code
|
|
170
257
|
let apiKey = SecureKey.apiKey.decryptedValue
|
|
258
|
+
let githubToken = key(for: .githubToken)
|
|
259
|
+
let sameGithubToken = key(.githubToken)
|
|
260
|
+
let keyFromString: SecureKey = "apiKey".secretKey
|
|
261
|
+
let valueFromString = "apiKey".secretKey.decryptedValue
|
|
262
|
+
let staticValue = String.key(for: .apiKey)
|
|
263
|
+
```
|
|
171
264
|
|
|
172
|
-
|
|
173
|
-
let someKey: String = key(for: .someKey)
|
|
265
|
+
Generated key names are camelized for Swift enum cases:
|
|
174
266
|
|
|
175
|
-
|
|
176
|
-
|
|
267
|
+
```text
|
|
268
|
+
api-key -> SecureKey.apiKey
|
|
269
|
+
githubToken -> SecureKey.githubToken
|
|
270
|
+
```
|
|
177
271
|
|
|
178
|
-
|
|
179
|
-
let apiKey: SecureKey = "apiKey".secretKey
|
|
272
|
+
## Output Files
|
|
180
273
|
|
|
181
|
-
|
|
182
|
-
let apiKey: String = "apiKey".secretKey.decryptedValue
|
|
274
|
+
The main output is:
|
|
183
275
|
|
|
184
|
-
|
|
185
|
-
|
|
276
|
+
```text
|
|
277
|
+
.secure-keys/SecureKeys.xcframework
|
|
186
278
|
```
|
|
187
279
|
|
|
188
|
-
|
|
280
|
+
Temporary Swift package and build files are created under `.secure-keys` during generation and removed after the framework is built.
|
|
281
|
+
|
|
282
|
+
## Security Notes
|
|
283
|
+
|
|
284
|
+
- Do not commit `.secure-keys/SecureKeys.xcframework` unless your release process intentionally requires it.
|
|
285
|
+
- Do not commit `.env` files or raw secret values.
|
|
286
|
+
- Treat app-bundled secrets as obfuscation, not as a perfect security boundary. A determined attacker can inspect a shipped app binary.
|
|
287
|
+
- Prefer server-side secret usage for highly sensitive credentials.
|
|
288
|
+
- Use environment-specific keys for development, staging, and production.
|
|
289
|
+
- Rotate keys regularly and revoke leaked credentials immediately.
|
|
290
|
+
|
|
291
|
+
## Secret Scanning and Validation
|
|
292
|
+
|
|
293
|
+
`secure-keys` ships both a CLI and a Ruby API for validating individual secret values and scanning source files or git diffs for accidentally exposed credentials.
|
|
189
294
|
|
|
190
|
-
###
|
|
295
|
+
### CLI
|
|
191
296
|
|
|
192
|
-
|
|
193
|
-
> You can see more information about the command using the `--help` option.
|
|
297
|
+
Scan the current directory:
|
|
194
298
|
|
|
195
299
|
```bash
|
|
196
|
-
secure-keys
|
|
300
|
+
secure-keys validate scan
|
|
301
|
+
```
|
|
197
302
|
|
|
198
|
-
|
|
199
|
-
Usage: secure-keys --xcframework [--options]
|
|
303
|
+
Scan a specific path:
|
|
200
304
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
-t, --target TARGET The target to add the xcframework
|
|
204
|
-
-r, --replace Replace the existing xcframework in the Xcode project (default: false)
|
|
205
|
-
-x, --xcodeproj XCODEPROJ The Xcode project path (default: the first found Xcode project)
|
|
305
|
+
```bash
|
|
306
|
+
secure-keys validate scan ./src
|
|
206
307
|
```
|
|
207
308
|
|
|
208
|
-
|
|
309
|
+
Scan only staged git changes (useful as a pre-commit hook):
|
|
209
310
|
|
|
210
311
|
```bash
|
|
211
|
-
secure-keys
|
|
312
|
+
secure-keys validate scan --staged
|
|
212
313
|
```
|
|
213
314
|
|
|
214
|
-
|
|
315
|
+
Save the report as JSON:
|
|
215
316
|
|
|
216
317
|
```bash
|
|
217
|
-
secure-keys
|
|
318
|
+
secure-keys validate scan --output report.json
|
|
218
319
|
```
|
|
219
320
|
|
|
220
|
-
|
|
221
|
-
> If you don't need to generate the `SecureKeys.xcframework` every time, you can use the `--no-generate` option.
|
|
321
|
+
Override the file extensions and exclusions:
|
|
222
322
|
|
|
223
323
|
```bash
|
|
224
|
-
secure-keys
|
|
324
|
+
secure-keys validate scan --extensions .rb,.swift,.go --excludes vendor,tmp,build
|
|
225
325
|
```
|
|
226
326
|
|
|
227
|
-
|
|
327
|
+
Enable verbose output:
|
|
228
328
|
|
|
229
329
|
```bash
|
|
230
|
-
secure-keys
|
|
330
|
+
secure-keys validate scan --verbose
|
|
231
331
|
```
|
|
232
332
|
|
|
233
|
-
|
|
234
|
-
> By default, the xcodeproj path would be the first found Xcode project.
|
|
333
|
+
Full option reference:
|
|
235
334
|
|
|
236
|
-
|
|
335
|
+
```text
|
|
336
|
+
Usage: secure-keys validate scan [path] [--options]
|
|
237
337
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
338
|
+
-h, --help Show help for the scan subcommand
|
|
339
|
+
--staged Scan staged git changes instead of a directory (default: false)
|
|
340
|
+
-o, --output FILE Save the scan report as JSON to FILE
|
|
341
|
+
--extensions Comma-separated file extensions to scan (e.g. .rb,.swift)
|
|
342
|
+
--excludes Comma-separated directory names to exclude from the scan
|
|
343
|
+
--verbose Enable verbose output (default: false)
|
|
344
|
+
```
|
|
244
345
|
|
|
245
|
-
|
|
246
|
-
export XCFRAMEWORK_TARGET="YourTargetName"
|
|
247
|
-
export XCFRAMEWORK_ADD=true
|
|
248
|
-
export XCFRAMEWORK_REPLACE=true
|
|
249
|
-
export XCFRAMEWORK_XCODEPROJ="/path/to/your/project.xcodeproj"
|
|
346
|
+
Exit codes:
|
|
250
347
|
|
|
251
|
-
|
|
252
|
-
|
|
348
|
+
| Code | Meaning |
|
|
349
|
+
|---|---|
|
|
350
|
+
| `0` | Scan completed with no findings |
|
|
351
|
+
| `1` | One or more secrets were detected |
|
|
352
|
+
|
|
353
|
+
### Validating a Secret
|
|
354
|
+
|
|
355
|
+
`SecureKeys::Validation::Validator` checks a single value against a set of security rules and returns a `ValidationResult`.
|
|
356
|
+
|
|
357
|
+
```ruby
|
|
358
|
+
require 'validation/validator'
|
|
359
|
+
|
|
360
|
+
validator = SecureKeys::Validation::Validator.new
|
|
361
|
+
result = validator.validate(key: :api_key, value: ENV['API_KEY'])
|
|
362
|
+
|
|
363
|
+
puts result.summary # ✅ api_key — no issues / ❌ api_key — 2 issue(s)
|
|
364
|
+
puts result.valid? # true / false
|
|
365
|
+
puts result.severity_level # :ok | :warning | :error | :critical
|
|
366
|
+
result.print # formatted report to stdout
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Available validation options:
|
|
370
|
+
|
|
371
|
+
| Option | Type | Default | Description |
|
|
372
|
+
|---|---|---|---|
|
|
373
|
+
| `check_entropy` | Boolean | `false` | Flag low-entropy (repetitive) values |
|
|
374
|
+
| `allow_production` | Boolean | `false` | Skip the production-key warning |
|
|
375
|
+
| `warn_on_pattern` | Boolean | `false` | Emit an informational notice when a pattern matches |
|
|
376
|
+
|
|
377
|
+
```ruby
|
|
378
|
+
result = validator.validate(
|
|
379
|
+
key: :stripe_key,
|
|
380
|
+
value: ENV['STRIPE_KEY'],
|
|
381
|
+
options: { check_entropy: true, warn_on_pattern: true }
|
|
382
|
+
)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Detect the type of a secret value:
|
|
386
|
+
|
|
387
|
+
```ruby
|
|
388
|
+
info = validator.detect_type(value: 'ghp_abc...')
|
|
389
|
+
# => { type: :github_token, description: "GitHub Personal Access Token", severity: :high, ... }
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Get provider-specific security recommendations:
|
|
393
|
+
|
|
394
|
+
```ruby
|
|
395
|
+
validator.recommendations(key: :githubToken)
|
|
396
|
+
# => ["Use GitHub Personal Access Tokens with minimal required scopes", ...]
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Scanning Files for Exposed Secrets
|
|
400
|
+
|
|
401
|
+
`SecureKeys::Validation::Scanner` scans source files or git diffs for credentials that match any of the 25+ built-in patterns.
|
|
402
|
+
|
|
403
|
+
Scan a directory:
|
|
404
|
+
|
|
405
|
+
```ruby
|
|
406
|
+
require 'validation/scanner'
|
|
407
|
+
|
|
408
|
+
scanner = SecureKeys::Validation::Scanner.new
|
|
409
|
+
result = scanner.scan_directory(path: '.')
|
|
410
|
+
|
|
411
|
+
puts result.clean? # true if no findings
|
|
412
|
+
puts result.files_count # number of files scanned
|
|
413
|
+
|
|
414
|
+
result.findings.each { |f| puts f.to_s }
|
|
415
|
+
result.print if !result.clean?
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Scan only staged git changes (useful in a pre-commit hook):
|
|
419
|
+
|
|
420
|
+
```ruby
|
|
421
|
+
result = scanner.scan_git_diff # staged only (default)
|
|
422
|
+
result = scanner.scan_git_diff(staged_only: false) # staged + unstaged
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Customize the scan at initialization or per call:
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
scanner = SecureKeys::Validation::Scanner.new(
|
|
429
|
+
options: {
|
|
430
|
+
extensions: ['.rb', '.swift', '.go'],
|
|
431
|
+
excludes: ['vendor', 'node_modules', '.git'],
|
|
432
|
+
max_depth: 5
|
|
433
|
+
}
|
|
434
|
+
)
|
|
253
435
|
```
|
|
254
436
|
|
|
255
|
-
|
|
437
|
+
Filter findings by severity:
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
result.by_severity(severity: :critical).each { |f| puts f.to_s }
|
|
441
|
+
```
|
|
256
442
|
|
|
257
|
-
|
|
443
|
+
### Detected Patterns
|
|
258
444
|
|
|
259
|
-
|
|
445
|
+
The scanner recognizes the following secret types out of the box:
|
|
260
446
|
|
|
261
|
-
|
|
447
|
+
| Pattern | Severity |
|
|
448
|
+
|---|---|
|
|
449
|
+
| GitHub personal / OAuth / App / refresh token | high |
|
|
450
|
+
| AWS access key ID | critical |
|
|
451
|
+
| AWS secret access key | critical |
|
|
452
|
+
| Google Cloud API key | high |
|
|
453
|
+
| Google OAuth token | high |
|
|
454
|
+
| Stripe secret key (live / test) | critical |
|
|
455
|
+
| Stripe publishable / restricted key | medium / high |
|
|
456
|
+
| Slack bot / app / webhook token | high / medium |
|
|
457
|
+
| JWT token | medium |
|
|
458
|
+
| PEM / RSA / EC / OpenSSH private key | critical |
|
|
459
|
+
| Generic API key assignment | medium |
|
|
460
|
+
| Generic secret / password assignment | medium |
|
|
461
|
+
| Firebase API key | medium |
|
|
462
|
+
| Twilio API key / Account SID | high / low |
|
|
463
|
+
| SendGrid API key | high |
|
|
464
|
+
| Mailchimp API key | medium |
|
|
465
|
+
| Square access token | high |
|
|
466
|
+
| PayPal Braintree access token | critical |
|
|
467
|
+
| Heroku API key | high |
|
|
468
|
+
| Base64-encoded secret | low |
|
|
469
|
+
| Suspicious assignment (catch-all) | low |
|
|
262
470
|
|
|
263
|
-
|
|
471
|
+
### Validation Configuration
|
|
264
472
|
|
|
265
|
-
|
|
473
|
+
All thresholds can be overridden with environment variables. Both the bare name and the `SECURE_KEYS_` prefix are supported.
|
|
266
474
|
|
|
267
|
-
|
|
475
|
+
| Environment variable | Default | Description |
|
|
476
|
+
|---|---|---|
|
|
477
|
+
| `SECURE_KEYS_API_KEY_LENGTH` | `20` | Minimum API key length |
|
|
478
|
+
| `SECURE_KEYS_TOKEN_LENGTH` | `20` | Minimum token length |
|
|
479
|
+
| `SECURE_KEYS_SECRET_LENGTH` | `16` | Minimum secret length |
|
|
480
|
+
| `SECURE_KEYS_PASSWORD_LENGTH` | `12` | Minimum password length |
|
|
481
|
+
| `SECURE_KEYS_KEY_LENGTH` | `16` | Minimum generic key length |
|
|
482
|
+
| `SECURE_KEYS_SCAN_EXTENSIONS` | `.swift,.rb,.py,.js,...` | Comma-separated file extensions to scan |
|
|
483
|
+
| `SECURE_KEYS_SCAN_EXCLUDES` | `.git,node_modules,Pods,...` | Comma-separated names to exclude |
|
|
484
|
+
| `SECURE_KEYS_MAX_SCAN_DEPTH` | `10` | Maximum directory traversal depth |
|
|
485
|
+
| `SECURE_KEYS_MIN_ENTROPY_THRESHOLD` | `3.0` | Shannon entropy threshold for `check_entropy` |
|
|
268
486
|
|
|
269
|
-
|
|
487
|
+
## Troubleshooting
|
|
270
488
|
|
|
271
|
-
|
|
489
|
+
`Error fetching the key from Keychain`
|
|
272
490
|
|
|
273
|
-
|
|
491
|
+
Verify the Keychain service, account, and identifier values. For the default configuration, the list must be stored with account `secure-keys` and service `secure-keys`.
|
|
274
492
|
|
|
275
|
-
|
|
493
|
+
`Error fetching the key from ENV variables`
|
|
276
494
|
|
|
277
|
-
|
|
495
|
+
Verify CI mode is enabled and that each configured key has a matching environment variable. For example, `github-token` maps to `GITHUB_TOKEN`.
|
|
496
|
+
|
|
497
|
+
`xcodebuild` fails
|
|
498
|
+
|
|
499
|
+
Verify Xcode command line tools are installed and selected:
|
|
278
500
|
|
|
279
501
|
```bash
|
|
280
|
-
|
|
281
|
-
$(SRCROOT)/.secure-keys
|
|
502
|
+
xcode-select -p
|
|
282
503
|
```
|
|
283
504
|
|
|
284
|
-
|
|
505
|
+
`SecureKeys.xcframework` is not found by Xcode
|
|
285
506
|
|
|
286
|
-
|
|
507
|
+
Verify `$(SRCROOT)/.secure-keys` is present in `Framework Search Paths` and that the framework is attached to the correct target.
|
|
287
508
|
|
|
288
|
-
|
|
289
|
-
2. Create a temporary `Swift Package` in the `.secure-keys` directory.
|
|
290
|
-
3. Copy the `SecureKeys` source code to the temporary `Swift Package`.
|
|
509
|
+
## Development
|
|
291
510
|
|
|
292
|
-
|
|
293
|
-
public enum SecureKey {
|
|
511
|
+
Install dependencies:
|
|
294
512
|
|
|
295
|
-
|
|
513
|
+
```bash
|
|
514
|
+
bundle install
|
|
515
|
+
```
|
|
296
516
|
|
|
297
|
-
|
|
298
|
-
case someKey
|
|
299
|
-
case unknown
|
|
517
|
+
Run the test suite:
|
|
300
518
|
|
|
301
|
-
|
|
519
|
+
```bash
|
|
520
|
+
bundle exec rspec
|
|
521
|
+
```
|
|
302
522
|
|
|
303
|
-
|
|
304
|
-
public var decryptedValue: String {
|
|
305
|
-
switch self {
|
|
306
|
-
case .apiKey: [1, 2, 4].decrypt(key: [248, 53, 26], iv: [148, 55, 47], tag: [119, 81])
|
|
307
|
-
case .someKey: [1, 2, 4].decrypt(key: [248, 53, 26], iv: [148, 55, 47], tag: [119, 81])
|
|
308
|
-
case .unknown: fatalError("Unknown key \(rawValue)")
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
```
|
|
523
|
+
Run the local CLI:
|
|
313
524
|
|
|
314
|
-
|
|
315
|
-
|
|
525
|
+
```bash
|
|
526
|
+
bundle exec ./bin/secure-keys --help
|
|
527
|
+
```
|
|
316
528
|
|
|
317
529
|
## License
|
|
318
530
|
|