iOfficeAI/OfficeCLI · error · ArgumentException

Invalid {contextKey} URL scheme '{scheme}:': only http, http

Error message

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.

What it means

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.

Source

Thrown at src/officecli/Core/HyperlinkUriValidator.cs:85

    /// URI whose scheme is in the allowlist. Used by the HTML preview, which
    /// must not throw on an authored-in HYPERLINK() formula but also must not
    /// emit a javascript:/data:/file: href as an XSS sink.
    /// </summary>
    public static bool IsSafeScheme(string url)
    {
        if (string.IsNullOrEmpty(url)) return false;
        if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return false;
        return !string.IsNullOrEmpty(uri.Scheme) && AllowedSchemes.Contains(uri.Scheme);
    }

    public static void RequireSafeScheme(string url, string contextKey = "link")
    {
        if (string.IsNullOrEmpty(url)) return;
        if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return; // not absolute → handler-internal path, not our concern
        var scheme = uri.Scheme;
        if (string.IsNullOrEmpty(scheme)) return;
        if (AllowedSchemes.Contains(scheme)) return;
        throw new ArgumentException(
            $"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.");
    }
}

View on GitHub (pinned to 1ced45e900)

Solutions

  1. 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.
  2. 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.
  3. If you are building a hyperlink from user input, sanitize or reject non-http(s)/mailto schemes upstream before calling Set/Add.
  4. For read-only inspection of an existing document's unsafe links, use query instead of set — the validator only fires on write operations.

Example fix

// before — rejected
set path='/body/p[1]/r[1]' hyperlink='javascript:void(0)'

// after — allowed
set path='/body/p[1]/r[1]' hyperlink='https://example.com'
// or for an email link:
set path='/body/p[1]/r[1]' hyperlink='mailto:nobody@example.com'
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate scheme before calling Set/Add hyperlink
using OfficeCli.Core;

bool IsHyperlinkSafe(string url)
{
    // Use the non-throwing predicate before the write
    return HyperlinkUriValidator.IsSafeScheme(url);
}

// Or call RequireSafeScheme explicitly to fail before the handler is invoked
try { HyperlinkUriValidator.RequireSafeScheme(candidateUrl, "link"); }
catch (ArgumentException) { /* reject input, log, or sanitize */ }

Try / catch

try
{
    HyperlinkUriValidator.RequireSafeScheme(url, contextKey: "link");
    handler.Set(path, new() { ["hyperlink"] = url });
}
catch (ArgumentException ex) when (ex.Message.Contains("URL scheme"))
{
    // Log the rejected URL and continue without the hyperlink
    Console.Error.WriteLine($"Rejected unsafe hyperlink: {url}");
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/fe92e3d4ca9b6802. Report an issue: GitHub.