commit 55786cd92516c578216465a7ee5eaba5288b950d
parent f350a371605a98cdad36026dcc699ae844782584
Author: Florian Dold <dold@taler.net>
Date: Tue, 21 Jul 2026 20:18:26 +0200
util: make codecForURLString validate its value
Diffstat:
2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/packages/taler-util/src/codec.ts b/packages/taler-util/src/codec.ts
@@ -493,6 +493,48 @@ export function codecForStringUnion<T extends Array<string>>(
}
/**
+ * Return a codec for a string that must be an absolute http(s) URL.
+ *
+ * Parsing alone is not enough for the fields this guards: they are opened by
+ * the browser or fetched over HTTP, and "javascript:", "data:" and "file:"
+ * are all perfectly well-formed URLs.
+ */
+export function codecForHttpUrlString(
+ shouldEndWithSlash?: boolean,
+): Codec<string> {
+ return {
+ decode(x: any, c?: Context): string {
+ if (typeof x !== "string") {
+ throw new DecodingError(
+ `expected string at ${renderContext(c)} but got ${typeof x}`,
+ );
+ }
+ if (shouldEndWithSlash && !x.endsWith("/")) {
+ throw new DecodingError(
+ `expected URL string that ends with slash at ${renderContext(
+ c,
+ )} but got ${x}`,
+ );
+ }
+ let url: URL;
+ try {
+ url = new URL(x);
+ } catch (e) {
+ throw new DecodingError(
+ `expected an absolute URL at ${renderContext(c)} but got "${x}"`,
+ );
+ }
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
+ throw new DecodingError(
+ `expected an http(s) URL at ${renderContext(c)} but got "${x}"`,
+ );
+ }
+ return x;
+ },
+ };
+}
+
+/**
* Return a codec for a value that must be a string.
*/
export function codecForStringURL(shouldEndWithSlash?: boolean): Codec<string> {
diff --git a/packages/taler-util/src/types-taler-common.ts b/packages/taler-util/src/types-taler-common.ts
@@ -38,6 +38,7 @@ import {
codecForList,
codecForMap,
codecForNumber,
+ codecForHttpUrlString,
codecForString,
codecOptional,
} from "./codec.js";
@@ -202,8 +203,11 @@ declare const __eddsapriv_str: unique symbol;
export type EddsaPrivateKeyString = string; // & { [__eddsapriv_str]: true };
export type CoinPublicKeyString = string;
-// FIXME: implement this codec
-export const codecForURLString = codecForString;
+/**
+ * Every field decoded with this is documented as an absolute URL that is
+ * either opened in a browser or used as the base of an HTTP request.
+ */
+export const codecForURLString = codecForHttpUrlString;
// FIXME: implement this codec
export const codecForLibtoolVersion = codecForString;
// FIXME: implement this codec