Skip to content
Merged
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
9 changes: 6 additions & 3 deletions src/lib/workos-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ export function createWorkOSClient(apiKey?: string, baseUrl?: string): WorkOSCLI
const key = apiKey ?? resolveApiKey();
const base = baseUrl ?? resolveApiBaseUrl();

// Parse hostname from base URL for SDK init
const hostname = new URL(base).hostname;
const sdk = new WorkOS(key, { apiHostname: hostname });
const url = new URL(base);
const sdk = new WorkOS(key, {
apiHostname: url.hostname,
...(url.port && { port: Number(url.port) }),
...(url.protocol === 'http:' && { https: false }),
});
Comment on lines +63 to +68
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate base format before using new URL to avoid opaque runtime failures.

At Line 63, new URL(base) assumes base is a full http(s) URL. Because endpoint values are user-provided and currently unvalidated upstream, malformed or shorthand inputs can throw/misparse and later fail as generic fetch failed. Add protocol/hostname validation with a clear error message at this boundary.

Suggested fix
-  const url = new URL(base);
+  let url: URL;
+  try {
+    url = new URL(base);
+  } catch {
+    throw new Error(`Invalid API base URL "${base}". Expected a full URL like "https://api.workos.com" or "http://localhost:4100".`);
+  }
+
+  if (!['http:', 'https:'].includes(url.protocol) || !url.hostname) {
+    throw new Error(`Invalid API base URL "${base}". Only http/https URLs with a hostname are supported.`);
+  }
+
   const sdk = new WorkOS(key, {
     apiHostname: url.hostname,
     ...(url.port && { port: Number(url.port) }),
     ...(url.protocol === 'http:' && { https: false }),
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const url = new URL(base);
const sdk = new WorkOS(key, {
apiHostname: url.hostname,
...(url.port && { port: Number(url.port) }),
...(url.protocol === 'http:' && { https: false }),
});
let url: URL;
try {
url = new URL(base);
} catch {
throw new Error(`Invalid API base URL "${base}". Expected a full URL like "https://api.workos.com" or "http://localhost:4100".`);
}
if (!['http:', 'https:'].includes(url.protocol) || !url.hostname) {
throw new Error(`Invalid API base URL "${base}". Only http/https URLs with a hostname are supported.`);
}
const sdk = new WorkOS(key, {
apiHostname: url.hostname,
...(url.port && { port: Number(url.port) }),
...(url.protocol === 'http:' && { https: false }),
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a pre-existing issue of the code, it currently doesn't handle it either and just crashes with ERR_INVALID_URL

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Langleu, that's a fair point — the previous new URL(base).hostname call had the exact same behaviour, so this PR doesn't regress anything there. I'll withdraw the concern for this PR.

Would you like me to open a follow-up GitHub issue to track adding proper URL validation (with a user-friendly error message) as a future improvement? It'd be a clean, standalone change that won't block this fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so it's already letting a user know that's not what is expected.
I think the better fix would be to adjust whatever is expected by the CLI in the first place vs just Custom API endpoint.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

Good point — improving the prompt/placeholder/description at the input level is a cleaner UX fix. Let me find where that prompt lives in the codebase.


🏁 Script executed:

#!/bin/bash
rg -rn "Custom API endpoint" --type ts -C5
rg -rn "Custom API endpoint" --type js -C5
rg -rn "baseUrl\|base_url\|apiEndpoint\|endpoint" --type ts -n -C3 | grep -i "prompt\|input\|question\|placeholder\|label" | head -40

Length of output: 1284


🏁 Script executed:

#!/bin/bash
# Find env command file
fd -e ts env src/commands/
# Look at the full env command
cat src/commands/env.ts 2>/dev/null || fd -e ts -e js "env" src/commands/ --exec cat {}
# Check bin.ts around endpoint option
rg -n "endpoint" src/bin.ts -C3

Length of output: 8944


@Langleu, agreed — improving the CLI-level description is the right place for this. Looking at the code, there are two spots that could be made more explicit:

  1. src/bin.ts Line 369 — the --endpoint flag description is just 'Custom API endpoint', which doesn't hint at the expected format:

    // before
    .option('endpoint', { type: 'string', describe: 'Custom API endpoint' })
    
    // after
    .option('endpoint', { type: 'string', describe: 'Custom API endpoint URL, e.g. http://localhost:4100' })
  2. src/utils/help-json.ts — the endpoint description there also appears to be a stale/truncated artifact ('n URL'), which would be worth fixing alongside.

  3. Interactive env add flow in src/commands/env.ts — the interactive wizard currently doesn't prompt for endpoint at all (only name, type, and API key). If the target audience for this is local emulator users who run workos env add interactively, it may be worth adding an optional prompt there too, with placeholder text showing the expected format:

    const endpointResult = await clack.text({
      message: 'Custom API endpoint URL (optional, e.g. http://localhost:4100)',
      placeholder: 'Leave blank for default (https://api.workos.com)',
    });
    if (!clack.isCancel(endpointResult) && endpointResult) {
      endpoint = endpointResult;
    }

Would you like me to open a follow-up issue to track the interactive-prompt gap and the help-json.ts description fix?


return {
sdk,
Expand Down
Loading