{"record":{"id":"fe92e3d4ca9b6802","repo":"iOfficeAI/OfficeCLI","slug":"invalid-contextkey-url-scheme-scheme-only","errorCode":null,"errorMessage":"Invalid {contextKey} URL scheme '{scheme}:': only http, https, mailto, ftp, ftps, sftp, news, tel, sms, file, about, and ppaction targets are accepted. javascript:, data:, vbscript:, and similar schemes are rejected to prevent click-bait redirection in shared documents.","messagePattern":"Invalid (.+?) URL scheme '(.+?):': only http, https, mailto, ftp, ftps, sftp, news, tel, sms, file, about, and ppaction targets are accepted\\. javascript:, data:, vbscript:, and similar schemes are rejected to prevent click-bait redirection in shared documents\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/HyperlinkUriValidator.cs","lineNumber":85,"sourceCode":"    /// URI whose scheme is in the allowlist. Used by the HTML preview, which\n    /// must not throw on an authored-in HYPERLINK() formula but also must not\n    /// emit a javascript:/data:/file: href as an XSS sink.\n    /// </summary>\n    public static bool IsSafeScheme(string url)\n    {\n        if (string.IsNullOrEmpty(url)) return false;\n        if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return false;\n        return !string.IsNullOrEmpty(uri.Scheme) && AllowedSchemes.Contains(uri.Scheme);\n    }\n\n    public static void RequireSafeScheme(string url, string contextKey = \"link\")\n    {\n        if (string.IsNullOrEmpty(url)) return;\n        if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return; // not absolute → handler-internal path, not our concern\n        var scheme = uri.Scheme;\n        if (string.IsNullOrEmpty(scheme)) return;\n        if (AllowedSchemes.Contains(scheme)) return;\n        throw new ArgumentException(\n            $\"Invalid {contextKey} URL scheme '{scheme}:': only http, https, mailto, ftp, ftps, sftp, news, tel, sms, file, about, and ppaction targets are accepted. \" +\n            \"javascript:, data:, vbscript:, and similar schemes are rejected to prevent click-bait redirection in shared documents.\");\n    }\n}\n","sourceCodeStart":67,"sourceCodeEnd":90,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/HyperlinkUriValidator.cs#L67-L90","documentation":"Thrown by HyperlinkUriValidator.RequireSafeScheme when an external hyperlink URL uses a URI scheme not in the allowlist (http, https, mailto, ftp, ftps, sftp, news, tel, sms, file, about, ppaction). The validator runs at write time to prevent dangerous schemes like javascript:, data:, and vbscript: from being embedded into shared Office documents where they could trigger script execution or data exfiltration on recipients. The check only applies to absolute URIs — handler-internal targets (slide://, fragment anchors, in-workbook refs) are resolved before the validator is consulted, so non-absolute URIs pass through silently.","triggerScenarios":"Calling any handler Set/Add operation that writes a hyperlink (e.g. set path='/body/p[1]/r[1]' hyperlink='javascript:alert(1)') with a URL whose scheme is not in AllowedSchemes. The RequireSafeScheme method is invoked after the URL has been classified as an external (absolute) URI. Passing 'data:text/html,...', 'vbscript:foo', or any custom scheme like 'myapp://' will trigger it. A null, empty, or non-absolute URI (relative path) does NOT trigger this — it returns early.","commonSituations":"Programmatic agents or LLM-generated batch scripts that construct hyperlink targets from untrusted user input or scraped HTML. Round-tripping a document that was originally authored by a tool that embeds javascript: or data: links. Copying a URL from a web page that uses data: URIs for inline assets. A dump→replay scenario where the source document contained a hyperlink with a scheme the validator rejects.","solutions":["Replace the disallowed scheme with an allowed one (http, https, mailto, ftp, ftps, sftp, news, tel, sms, file, about, ppaction) that serves the same intent.","If the link is handler-internal navigation (e.g. PowerPoint slide jump), use the handler's internal notation (ppaction://, slide://, #anchor) which is resolved before the validator is consulted — do not pass it as an absolute external URI.","If you are building a hyperlink from user input, sanitize or reject non-http(s)/mailto schemes upstream before calling Set/Add.","For read-only inspection of an existing document's unsafe links, use query instead of set — the validator only fires on write operations."],"exampleFix":"// before — rejected\nset path='/body/p[1]/r[1]' hyperlink='javascript:void(0)'\n\n// after — allowed\nset path='/body/p[1]/r[1]' hyperlink='https://example.com'\n// or for an email link:\nset path='/body/p[1]/r[1]' hyperlink='mailto:nobody@example.com'","handlingStrategy":"validation","validationCode":"// Pre-validate scheme before calling Set/Add hyperlink\nusing OfficeCli.Core;\n\nbool IsHyperlinkSafe(string url)\n{\n    // Use the non-throwing predicate before the write\n    return HyperlinkUriValidator.IsSafeScheme(url);\n}\n\n// Or call RequireSafeScheme explicitly to fail before the handler is invoked\ntry { HyperlinkUriValidator.RequireSafeScheme(candidateUrl, \"link\"); }\ncatch (ArgumentException) { /* reject input, log, or sanitize */ }","typeGuard":null,"tryCatchPattern":"try\n{\n    HyperlinkUriValidator.RequireSafeScheme(url, contextKey: \"link\");\n    handler.Set(path, new() { [\"hyperlink\"] = url });\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"URL scheme\"))\n{\n    // Log the rejected URL and continue without the hyperlink\n    Console.Error.WriteLine($\"Rejected unsafe hyperlink: {url}\");\n}","preventionTips":["Always use the non-throwing IsSafeScheme predicate to pre-filter user-supplied URLs before passing them to Set/Add.","When building hyperlinks programmatically, default to http/https and reject any input that doesn't start with a known-safe scheme prefix.","If accepting user URLs in an agent pipeline, sanitize to http(s) only unless you have an explicit reason to allow mailto/tel/etc."],"tags":["security","hyperlink","xss-prevention","scheme-validation","write-time-guard"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}