Remove URL-unsafe characters from gen-secret charset
Remove ^, +, ~, :, @ from the allowed charset. The ^ character breaks SQLAlchemy DATABASE_URL parsing, + becomes space in URL query strings, : and @ are URL delimiters. The remaining charset (A-Za-z0-9._-) is safe in URLs, database connection strings, YAML, JSON, and shell without any encoding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
CHARSET='A-Za-z0-9._+\-:@^~'
|
CHARSET='A-Za-z0-9._\-'
|
||||||
DEFAULT_LENGTH=32
|
DEFAULT_LENGTH=32
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
Usage: gen-secret [OPTIONS] [LENGTH]
|
Usage: gen-secret [OPTIONS] [LENGTH]
|
||||||
|
|
||||||
Generate a cryptographically random string safe for bash, YAML, and JSON.
|
Generate a cryptographically random string safe for bash, YAML, JSON, and URLs.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
LENGTH Number of characters (default: 32)
|
LENGTH Number of characters (default: 32)
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ gen-secret [OPTIONS] [LENGTH]
|
|||||||
The output uses only characters that need no escaping in bash (unquoted assignment), YAML (plain scalar), and JSON (string value):
|
The output uses only characters that need no escaping in bash (unquoted assignment), YAML (plain scalar), and JSON (string value):
|
||||||
|
|
||||||
```
|
```
|
||||||
A-Z a-z 0-9 . _ + - : @ ^ ~
|
A-Z a-z 0-9 . _ -
|
||||||
```
|
```
|
||||||
|
|
||||||
**Excluded** (unsafe in at least one context): `"`, `'`, `\`, `` ` ``, `$`, `!`, `{`, `}`, `(`, `)`, `[`, `]`, `#`, `%`, `&`, `|`, `<`, `>`, `*`, `?`, `;`, `,`, `=`, space, tab, newline, `/`
|
**Excluded** (unsafe in at least one context): `"`, `'`, `\`, `` ` ``, `$`, `!`, `{`, `}`, `(`, `)`, `[`, `]`, `#`, `%`, `&`, `|`, `<`, `>`, `*`, `?`, `;`, `,`, `=`, `+`, `^`, `~`, `:`, `@`, space, tab, newline, `/`
|
||||||
|
|
||||||
Note: `/` is excluded because YAML plain scalars starting with `//` or containing `#` after a space can cause issues, and removing `/` keeps the set simpler without meaningful entropy loss.
|
Note: `^` breaks URL parsing (e.g., SQLAlchemy DATABASE_URL). `+` becomes space in URL query strings. `:` and `@` are URL delimiters (userinfo/host separators). `~` can be shell-expanded. These are excluded to ensure secrets are safe in database connection strings, URLs, and shell contexts without encoding.
|
||||||
|
|
||||||
## Behaviour
|
## Behaviour
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ Note: `/` is excluded because YAML plain scalars starting with `//` or containin
|
|||||||
When `--dryrun` or `-n` is passed:
|
When `--dryrun` or `-n` is passed:
|
||||||
|
|
||||||
```
|
```
|
||||||
[dryrun] Would generate a 32-character secret from charset: [A-Za-z0-9._+\-:@^~]
|
[dryrun] Would generate a 32-character secret from charset: [A-Za-z0-9._\-]
|
||||||
```
|
```
|
||||||
|
|
||||||
(Substituting the actual length if provided.)
|
(Substituting the actual length if provided.)
|
||||||
@@ -70,13 +70,13 @@ No random output is produced.
|
|||||||
```bash
|
```bash
|
||||||
# Default 32-character secret
|
# Default 32-character secret
|
||||||
$ gen-secret
|
$ gen-secret
|
||||||
xQ9.kT3+mR7:nW2@pF5^bY8~cH4_dL6a
|
xQ9.kT3-mR7_nW2.pF5-bY8_cH4.dL6a
|
||||||
|
|
||||||
# Custom length
|
# Custom length
|
||||||
$ gen-secret 64
|
$ gen-secret 64
|
||||||
xQ9.kT3+mR7:nW2@pF5^bY8~cH4_dL6axQ9.kT3+mR7:nW2@pF5^bY8~cH4_dL6a
|
xQ9.kT3-mR7_nW2.pF5-bY8_cH4.dL6axQ9.kT3-mR7_nW2.pF5-bY8_cH4.dL6a
|
||||||
|
|
||||||
# Dryrun
|
# Dryrun
|
||||||
$ gen-secret -n 16
|
$ gen-secret -n 16
|
||||||
[dryrun] Would generate a 16-character secret from charset: [A-Za-z0-9._+\-:@^~]
|
[dryrun] Would generate a 16-character secret from charset: [A-Za-z0-9._\-]
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -57,12 +57,12 @@ echo
|
|||||||
|
|
||||||
out=$("$GEN_SECRET" --dryrun 2>&1)
|
out=$("$GEN_SECRET" --dryrun 2>&1)
|
||||||
assert_eq "dryrun default length" \
|
assert_eq "dryrun default length" \
|
||||||
'[dryrun] Would generate a 32-character secret from charset: [A-Za-z0-9._+\-:@^~]' \
|
'[dryrun] Would generate a 32-character secret from charset: [A-Za-z0-9._\-]' \
|
||||||
"$out"
|
"$out"
|
||||||
|
|
||||||
out=$("$GEN_SECRET" -n 16 2>&1)
|
out=$("$GEN_SECRET" -n 16 2>&1)
|
||||||
assert_eq "dryrun custom length" \
|
assert_eq "dryrun custom length" \
|
||||||
'[dryrun] Would generate a 16-character secret from charset: [A-Za-z0-9._+\-:@^~]' \
|
'[dryrun] Would generate a 16-character secret from charset: [A-Za-z0-9._\-]' \
|
||||||
"$out"
|
"$out"
|
||||||
|
|
||||||
# --- Help ---
|
# --- Help ---
|
||||||
@@ -78,7 +78,7 @@ out=$("$GEN_SECRET" 2>&1)
|
|||||||
rc=$?
|
rc=$?
|
||||||
assert_exit "default exits 0" 0 "$rc"
|
assert_exit "default exits 0" 0 "$rc"
|
||||||
assert_eq "default length is 32" 32 "${#out}"
|
assert_eq "default length is 32" 32 "${#out}"
|
||||||
assert_match "default uses safe charset" '^[A-Za-z0-9._+:@^~-]+$' "$out"
|
assert_match "default uses safe charset" '^[A-Za-z0-9._-]+$' "$out"
|
||||||
|
|
||||||
# --- Custom length ---
|
# --- Custom length ---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user