Repository navigation
fix(sdk): make a cli_unauthenticated refusal say why the probe failed #536
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
Changes from all commits
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 |
|---|---|---|
|
|
@@ -478,36 +478,70 @@ function probeCli( | |
| modelCommand, | ||
| }; | ||
| } | ||
| const authStatus = probe(auth).status; | ||
| const authProbe = probe(auth); | ||
| const authenticated = authProbe.status === 0; | ||
| return { | ||
| exists: true, | ||
| supported: true, | ||
| authenticated: authStatus === 0, | ||
| authenticated, | ||
| modelAvailable: false, | ||
| authCommand, | ||
| modelCommand, | ||
| // Only on failure: on success there is nothing to explain, and the output | ||
| // is the most identity-bearing thing this function touches. | ||
| ...(authenticated | ||
| ? {} | ||
| : { | ||
| authExitCode: authProbe.status, | ||
| authFailureDetail: redactProbeOutput( | ||
| `${authProbe.stderr}${authProbe.stdout}`, | ||
| ).trim().slice(0, 500), | ||
| }), | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Redact anything that looks like a credential or an account identifier. | ||
| * | ||
| * `auth status` output is diagnostic, but it is also the one place an account | ||
| * email, org id or token fragment can appear. The point of surfacing it is to | ||
| * say WHY a probe failed, which survives redaction; leaking an identity into a | ||
| * refusal message that gets pasted into issues does not. | ||
| */ | ||
| function redactProbeOutput(text: string): string { | ||
| return text | ||
| .replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, '<redacted-email>') | ||
| .replace(/\b(sk|pk|oat|rt)[-_][A-Za-z0-9._-]{8,}/gi, '<redacted-token>') | ||
| .replace(/\b[A-Fa-f0-9]{32,}\b/g, '<redacted-hex>'); | ||
|
Comment on lines
+488
to
+492
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.
If a failing provider or custom wrapper includes a credential in stderr, this new redactor exposes many secret formats already recognized elsewhere in this SDK, including Relay Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| function runProbe( | ||
| executable: string, | ||
| directory: string, | ||
| invocation: CliInvocation, | ||
| environment: NodeJS.ProcessEnv = process.env, | ||
| ): { status: number | null; stdout: string } { | ||
| ): { status: number | null; stdout: string; stderr: string } { | ||
| const env = { ...environment }; | ||
| delete env[MODEL_ENV]; | ||
| if (invocation.modelEnv !== undefined) env[MODEL_ENV] = invocation.modelEnv; | ||
| const result = spawnSync(executable, invocation.args, { | ||
| cwd: directory, | ||
| encoding: 'utf8', | ||
| stdio: ['ignore', 'pipe', 'ignore'], | ||
| // stderr was 'ignore'. A failing `auth status` writes its reason there, so | ||
| // discarding it made every authentication refusal structurally | ||
| // undiagnosable: the refusal could say a probe exited non-zero and never | ||
| // what it said. Captured, then redacted at the point of use. | ||
| stdio: ['ignore', 'pipe', 'pipe'], | ||
| timeout: invocation.timeoutMs, | ||
| env, | ||
| }); | ||
| const failure = classifySpawnFailure(result.error, result.signal, invocation.timeoutMs); | ||
| if (failure !== undefined) throw failure; | ||
| return { status: result.status, stdout: result.stdout }; | ||
| return { | ||
| status: result.status, | ||
| stdout: result.stdout, | ||
| stderr: result.stderr ?? '', | ||
|
Comment on lines
+540
to
+543
Contributor
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. 🟡 Model-less auth failures stay unexplained Without a model, Learn moreThe new output fields are returned from Example: A Recommended fix: Store the full authentication result in the model-less branch and populate the same redacted failure fields used by the model-scoped branch. Add an end-to-end test using a failing executable so stderr capture and redaction are both exercised. Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| }; | ||
| } | ||
|
|
||
| function resolveExecutable(command: string, directory: string): string | undefined { | ||
|
|
||
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.
🟥 Authentication diagnostics can expose credentials
Failed auth output enters
authFailureDetailafter narrow pattern redaction. Unmatched API keys, bearer tokens, cookies, usernames, and organization identifiers then reach reports and errors.Was this helpful? React with 👍 or 👎 to provide feedback.