Repository navigation
fix(cli): reject a SECRET that cannot be saved to .env and read back unchanged #1091
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6b3db4d
fcb29a8
f2183ec
d97bc6e
f463468
c306598
816470e
17d8658
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "nostream": patch | ||
| --- | ||
|
|
||
| fix(cli): reject a SECRET that cannot be saved to .env and read back unchanged |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,6 +135,41 @@ const validateBoolean = (key: string, value: string): string | undefined => { | |
| return `${key} must be true or false` | ||
| } | ||
|
|
||
| export const stripQuotes = (value: string): string => { | ||
| const trimmed = value.trim() | ||
| if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { | ||
| return trimmed.slice(1, -1).trim() | ||
| } | ||
| return trimmed | ||
| } | ||
|
|
||
| /** | ||
| * Why a secret cannot be written to `.env` and read back unchanged, or undefined when it can. An unquoted `#` starts a | ||
| * comment for the loader, a newline ends the line, a `$` is expanded by Docker Compose, and a quote the loader cannot | ||
| * match changes what it reads. | ||
| */ | ||
| export const getUnsafeSecretReason = (secret: string): string | undefined => { | ||
| if (secret.includes('#')) { | ||
| return 'SECRET cannot contain "#"' | ||
| } | ||
| if (/[\r\n]/.test(secret)) { | ||
| return 'SECRET cannot contain newlines' | ||
| } | ||
| if (secret.includes('$')) { | ||
| // Docker Compose reads the same .env and expands `$NAME`, `${NAME}` and `$$`, which Node's loader leaves alone. | ||
| return 'SECRET cannot contain "$"' | ||
| } | ||
| if (/^["'`]/.test(stripQuotes(secret))) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return 'SECRET cannot start with an unmatched quote' | ||
| } | ||
| const trimmed = secret.trim() | ||
| if (stripQuotes(trimmed) !== trimmed && stripQuotes(trimmed).includes(trimmed[0])) { | ||
| // The loader ends a quoted value at the first closing quote, so a quote inside would cut the secret short. | ||
| return 'SECRET cannot contain the quote it is wrapped in' | ||
|
Priyanshubhartistm marked this conversation as resolved.
Resolved
|
||
| } | ||
| return undefined | ||
| } | ||
|
|
||
| export const validateEnvPair = (key: string, value: string): string | undefined => { | ||
| if (!isSupportedEnvKey(key)) { | ||
| return `${key} is not a supported environment setting` | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reject or normalize quoted escape sequences before saving.
SECRETvalue"abc\ndef"passes this check and is saved with its quotes. Node expands\nto a newline, but the setup parser reads a literal backslash andn. Starts through those paths can therefore use different secrets and derived keys. Validate the serialized value against both readers, or normalize it before saving. (raw.githubusercontent.com)