test: cover nextcloud proxy url handling
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
"dev": "node --watch dist/index.js",
|
"dev": "node --watch dist/index.js",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
|
"test": "npm run build && node --test dist/server/nextcloud-proxy.test.js",
|
||||||
"check": "tsc -p tsconfig.json --noEmit"
|
"check": "tsc -p tsconfig.json --noEmit"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
61
src/server/nextcloud-proxy.test.ts
Normal file
61
src/server/nextcloud-proxy.test.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "node:test";
|
||||||
|
import {
|
||||||
|
parseHttpUrl,
|
||||||
|
resolveDavUrlFromShareUrl,
|
||||||
|
resolveValidatedBlobUrl
|
||||||
|
} from "./nextcloud-proxy.js";
|
||||||
|
|
||||||
|
describe("Nextcloud public share URL handling", () => {
|
||||||
|
it("resolves public share URLs to DAV roots", () => {
|
||||||
|
assert.equal(
|
||||||
|
resolveDavUrlFromShareUrl("https://cloud.example.com/index.php/s/share-token"),
|
||||||
|
"https://cloud.example.com/public.php/dav/files/share-token/"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps DAV share URLs normalized", () => {
|
||||||
|
assert.equal(
|
||||||
|
resolveDavUrlFromShareUrl("https://cloud.example.com/public.php/dav/files/share-token/photos"),
|
||||||
|
"https://cloud.example.com/public.php/dav/files/share-token/"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects non-HTTP URLs and embedded credentials", () => {
|
||||||
|
assert.throws(
|
||||||
|
() => parseHttpUrl("file:///tmp/photo.jpg", "Invalid image URL."),
|
||||||
|
/Invalid image URL/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => parseHttpUrl("https://user:pass@cloud.example.com/index.php/s/share-token", "Invalid image URL."),
|
||||||
|
/URL credentials are not allowed/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows image URLs inside the selected share", () => {
|
||||||
|
assert.equal(
|
||||||
|
resolveValidatedBlobUrl(
|
||||||
|
"https://cloud.example.com/public.php/dav/files/share-token/folder/photo.jpg",
|
||||||
|
"https://cloud.example.com/index.php/s/share-token"
|
||||||
|
),
|
||||||
|
"https://cloud.example.com/public.php/dav/files/share-token/folder/photo.jpg"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects image URLs outside the selected share", () => {
|
||||||
|
assert.throws(
|
||||||
|
() => resolveValidatedBlobUrl(
|
||||||
|
"https://cloud.example.com/public.php/dav/files/other-token/photo.jpg",
|
||||||
|
"https://cloud.example.com/index.php/s/share-token"
|
||||||
|
),
|
||||||
|
/outside the requested Nextcloud share/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => resolveValidatedBlobUrl(
|
||||||
|
"https://other.example.com/public.php/dav/files/share-token/photo.jpg",
|
||||||
|
"https://cloud.example.com/index.php/s/share-token"
|
||||||
|
),
|
||||||
|
/outside the requested Nextcloud share/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ServerResponse } from "node:http";
|
import type { ServerResponse } from "node:http";
|
||||||
|
|
||||||
function parseHttpUrl(value: string, errorMessage: string): URL {
|
export function parseHttpUrl(value: string, errorMessage: string): URL {
|
||||||
const url = new URL(value.trim());
|
const url = new URL(value.trim());
|
||||||
|
|
||||||
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
||||||
@@ -14,7 +14,7 @@ function parseHttpUrl(value: string, errorMessage: string): URL {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveDavUrlFromShareUrl(shareUrl: string): string {
|
export function resolveDavUrlFromShareUrl(shareUrl: string): string {
|
||||||
const url = parseHttpUrl(shareUrl, "Please enter a public Nextcloud share link.");
|
const url = parseHttpUrl(shareUrl, "Please enter a public Nextcloud share link.");
|
||||||
const publicShare = url.pathname.match(/\/s\/([^/?#]+)/);
|
const publicShare = url.pathname.match(/\/s\/([^/?#]+)/);
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ function resolveDavUrlFromShareUrl(shareUrl: string): string {
|
|||||||
throw new Error("Please enter a public Nextcloud share link.");
|
throw new Error("Please enter a public Nextcloud share link.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveValidatedBlobUrl(targetUrl: string, shareUrl: string): string {
|
export function resolveValidatedBlobUrl(targetUrl: string, shareUrl: string): string {
|
||||||
const target = parseHttpUrl(targetUrl, "Invalid image URL.");
|
const target = parseHttpUrl(targetUrl, "Invalid image URL.");
|
||||||
const shareDavUrl = new URL(resolveDavUrlFromShareUrl(shareUrl));
|
const shareDavUrl = new URL(resolveDavUrlFromShareUrl(shareUrl));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user