{"record":{"id":"4b5b95b01d8639a0","repo":"jackwener/OpenCLI","slug":"label-must-be-a-linkedin-url","errorCode":null,"errorMessage":"${label} must be a LinkedIn URL","messagePattern":"(.+?) must be a LinkedIn URL","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/shared.js","lineNumber":93,"sourceCode":"  }\n  return text;\n}\n\nexport function looksLinkedInAuthWall(value) {\n  const text = normalizeWhitespace(value).toLowerCase();\n  if (!text) return false;\n  return /linkedin\\.com\\/(?:login|checkpoint|authwall|uas)/i.test(text)\n    || /\\b(sign in|log in|join linkedin|captcha|verification required)\\b/i.test(text)\n    || /(请登录|登录领英|安全验证)/.test(text);\n}\n\nexport function assertSafeLinkedinUrl(value, label, fallbackPath = '/') {\n  const raw = normalizeWhitespace(value || `https://www.linkedin.com${fallbackPath}`);\n  let parsed;\n  try {\n    parsed = new URL(raw, 'https://www.linkedin.com');\n  } catch {\n    throw new ArgumentError(`${label} must be a LinkedIn URL`);\n  }\n  const host = parsed.hostname.toLowerCase();\n  if (parsed.protocol !== 'https:' || parsed.username || parsed.password || parsed.port) {\n    throw new ArgumentError(`${label} must be an https LinkedIn URL without credentials or port`);\n  }\n  if (host !== 'linkedin.com' && host !== 'www.linkedin.com') {\n    throw new ArgumentError(`${label} must point to linkedin.com`);\n  }\n  return parsed.toString();\n}\n\nexport function requireStringArg(args, key, label = key) {\n  const value = normalizeWhitespace(args?.[key]);\n  if (!value) throw new ArgumentError(`${label} is required`);\n  return value;\n}\n\nexport function parseLimit(value, fallback, max) {","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/shared.js#L75-L111","documentation":"assertSafeLinkedinUrl parses the supplied URL against https://www.linkedin.com and throws ArgumentError when parsing fails entirely — the value is not a valid URL at all. This is the first gate of the LinkedIn URL safety validation used by commands like services-read.","triggerScenarios":"Passing a value that cannot be parsed by `new URL(raw, 'https://www.linkedin.com')`, e.g. a whitespace-free garbage string like '::::' or '%zz', or an empty label with no fallback applied (empty values fall back to https://www.linkedin.com<fallbackPath>, so pure emptiness does not trigger this).","commonSituations":"Typos in config/env-provided URLs; passing a raw handle like 'jane-doe' is accepted (resolved relative to linkedin.com) but fragments like 'https://' alone or 'http\\x' fail; quoting/space issues in shell args are normalized away, leaving invalid remnants.","solutions":["Validate the URL string with new URL(value, 'https://www.linkedin.com') before calling the command.","Pass a full absolute https URL, e.g. https://www.linkedin.com/in/<handle>/.","If the value comes from config/env, log and sanitize it (trim, percent-encode) before passing."],"exampleFix":"// before\nawait runCommand('linkedin services-read', ['--profile-url', 'https://']);\n// after\nawait runCommand('linkedin services-read', ['--profile-url', 'https://www.linkedin.com/in/jane-doe/']);","handlingStrategy":"validation","validationCode":"function parseLinkedinUrl(v) {\n  try { return new URL(String(v).trim(), 'https://www.linkedin.com'); }\n  catch { throw new Error('profile-url/services-url must be a valid URL'); }\n}","typeGuard":"function isValidUrl(v) {\n  try { new URL(String(v), 'https://www.linkedin.com'); return true; } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Validate URLs from env/config at startup, fail fast with a clear message.","Trim and sanitize shell-supplied arguments before passing them.","Build URLs programmatically instead of hand-typing them."],"tags":["linkedin","argument-validation","url"],"backgroundTag":"invalid-url-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}