jackwener/OpenCLI · error · ArgumentError

${label} must be an https LinkedIn URL without credentials o

Error message

${label} must be an https LinkedIn URL without credentials or port

What it means

After parsing succeeds, assertSafeLinkedinUrl enforces transport hardening: the URL must use https, and must not embed username/password credentials or a non-default port. Violating any of these raises this ArgumentError, preventing SSRF-style abuse and credential leakage in URLs handed to the scraper.

Source

Thrown at clis/linkedin/shared.js:97

export function looksLinkedInAuthWall(value) {
  const text = normalizeWhitespace(value).toLowerCase();
  if (!text) return false;
  return /linkedin\.com\/(?:login|checkpoint|authwall|uas)/i.test(text)
    || /\b(sign in|log in|join linkedin|captcha|verification required)\b/i.test(text)
    || /(请登录|登录领英|安全验证)/.test(text);
}

export function assertSafeLinkedinUrl(value, label, fallbackPath = '/') {
  const raw = normalizeWhitespace(value || `https://www.linkedin.com${fallbackPath}`);
  let parsed;
  try {
    parsed = new URL(raw, 'https://www.linkedin.com');
  } catch {
    throw new ArgumentError(`${label} must be a LinkedIn URL`);
  }
  const host = parsed.hostname.toLowerCase();
  if (parsed.protocol !== 'https:' || parsed.username || parsed.password || parsed.port) {
    throw new ArgumentError(`${label} must be an https LinkedIn URL without credentials or port`);
  }
  if (host !== 'linkedin.com' && host !== 'www.linkedin.com') {
    throw new ArgumentError(`${label} must point to linkedin.com`);
  }
  return parsed.toString();
}

export function requireStringArg(args, key, label = key) {
  const value = normalizeWhitespace(args?.[key]);
  if (!value) throw new ArgumentError(`${label} is required`);
  return value;
}

export function parseLimit(value, fallback, max) {
  if (value === undefined || value === null || value === '') return fallback;
  const parsed = Number(value);
  if (!Number.isInteger(parsed) || parsed < 1 || parsed > max) {
    throw new ArgumentError(`--limit must be an integer between 1 and ${max}`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Use plain https URLs without credentials or ports: https://www.linkedin.com/in/<handle>/.
  2. Strip credentials/port before calling: new URL(raw); u.protocol='https:'; u.username=u.password=''; u.port=''.
  3. If you need a proxy, configure it at the HTTP-client/browser level, not in the URL.

Example fix

// before
await runCommand('linkedin services-read', ['--profile-url', 'http://user:pw@www.linkedin.com:8443/in/jane/']);
// after
await runCommand('linkedin services-read', ['--profile-url', 'https://www.linkedin.com/in/jane-doe/']);
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeLinkedInUrl(raw) {
  const u = new URL(String(raw).trim(), 'https://www.linkedin.com');
  u.protocol = 'https:'; u.username = ''; u.password = ''; u.port = '';
  return u.toString();
}
args['profile-url'] = sanitizeLinkedInUrl(args['profile-url']);

Type guard

function isHardenedHttpsUrl(v) {
  try {
    const u = new URL(String(v));
    return u.protocol === 'https:' && !u.username && !u.password && !u.port;
  } catch { return false; }
}

Prevention

When it happens

Trigger: Passing http:// instead of https://; embedding credentials (https://user:pass@linkedin.com/...); specifying an explicit port (https://www.linkedin.com:8443/in/me/) — any of these triggers the check `protocol !== 'https:' || username || password || port`.

Common situations: Constructing URLs from templates that default to http; pasting URLs that include basic-auth credentials for a proxy; local dev proxy setups that append :port and are mistakenly reused for LinkedIn args.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/c66b35d0ddde36bd. Report an issue: GitHub.