Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions src/cli/system-credential-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,41 @@ function macOsCredentialStore(
label: "artifact-server-cli-profile",
})),
),
// `security` takes the `-w` value only from argv or a terminal prompt,
// never from stdin, so the piped secret was stored as an empty password
// and every later read came back blank. `security -i` reads whole
// commands from stdin, which keeps the secret out of argv.
write: (account, secret) => runCredentialProcess({
arguments: [
arguments: ["-i"],
environment,
executable: "/usr/bin/security",
input: [
"add-generic-password",
"-a",
account,
quoteForSecurity(account),
"-s",
credentialService,
quoteForSecurity(credentialService),
"-U",
"-w",
],
environment,
executable: "/usr/bin/security",
input: `${Redacted.value(secret)}\n`,
quoteForSecurity(Redacted.value(secret)),
].join(" ") + "\n",
operation: "write",
}).pipe(Effect.asVoid),
};
}

/**
* Quote one argument for `security -i`. Stored values are JSON of base64url
* or hex tokens and UUID accounts, so a single quote or newline never occurs;
* refuse rather than guess at the interactive tokenizer's escaping.
*/
function quoteForSecurity(value: string): string {
if (value.includes("'") || value.includes("\n")) {
throw new Error("A credential value cannot contain a single quote or newline.");
}
return `'${value}'`;
}

function linuxCredentialStore(
environment: NodeJS.ProcessEnv,
): CliCredentialStoreOperations {
Expand Down