This repository has been archived on 2023-01-14. You can view files and clone it, but cannot push or open issues or pull requests.
libsiaweb/src/skylinkValidate.ts

131 lines
4.0 KiB
TypeScript

import { addContextToErr } from "./err.js";
import { b64ToBuf } from "./encoding.js";
import {
SKYLINK_U8_V1_V2_LENGTH,
parseSkylinkBitfield,
} from "./skylinkBitfield.js";
import { Err } from "./types.js";
// validateSkyfilePath checks whether the provided path is a valid path for a
// file in a skylink.
function validateSkyfilePath(path: string): string | null {
if (path === "") {
return "path cannot be blank";
}
if (path === "..") {
return "path cannot be ..";
}
if (path === ".") {
return "path cannot be .";
}
if (path.startsWith("/")) {
return "metdata.Filename cannot start with /";
}
if (path.startsWith("../")) {
return "metdata.Filename cannot start with ../";
}
if (path.startsWith("./")) {
return "metdata.Filename cannot start with ./";
}
const pathElems = path.split("/");
for (let i = 0; i < pathElems.length; i++) {
if (pathElems[i] === ".") {
return "path cannot have a . element";
}
if (pathElems[i] === "..") {
return "path cannot have a .. element";
}
if (pathElems[i] === "") {
return "path cannot have an empty element, cannot contain //";
}
}
return null;
}
// validateSkyfileMetadata checks whether the provided metadata is valid
// metadata for a skyfile.
function validateSkyfileMetadata(metadata: any): string | null {
// Check that the filename is valid.
if (!("Filename" in metadata)) {
return "metadata.Filename does not exist";
}
if (typeof metadata.Filename !== "string") {
return "metadata.Filename is not a string";
}
const errVSP = validateSkyfilePath(metadata.Filename);
if (errVSP !== null) {
return addContextToErr(
errVSP,
"metadata.Filename does not have a valid path"
);
}
// Check that there are no subfiles.
if ("Subfiles" in metadata) {
// TODO: Fill this out using code from
// skymodules.ValidateSkyfileMetadata to support subfiles.
return "cannot upload files that have subfiles";
}
// Check that the default path rules are being respected.
if ("DisableDefaultPath" in metadata && "DefaultPath" in metadata) {
return "cannot set both a DefaultPath and also DisableDefaultPath";
}
if ("DefaultPath" in metadata) {
// TODO: Fill this out with code from
// skymodules.validateDefaultPath to support subfiles and
// default paths.
return "cannot set a default path if there are no subfiles";
}
if ("TryFiles" in metadata) {
if (!metadata.TryFiles.IsArray()) {
return "metadata.TryFiles must be an array";
}
if (metadata.TryFiles.length === 0) {
return "metadata.TryFiles should not be empty";
}
if ("DefaultPath" in metadata) {
return "metadata.TryFiles cannot be used alongside DefaultPath";
}
if ("DisableDefaultPath" in metadata) {
return "metadata.TryFiles cannot be used alongside DisableDefaultPath";
}
// TODO: finish the TryFiles checking using skymodules.ValidateTryFiles
return "TryFiles is not supported at this time";
}
if ("ErrorPages" in metadata) {
// TODO: finish using skymodules.ValidateErrorPages
return "ErrorPages is not supported at this time";
}
return null;
}
// validateSkylink returns null if the provided Uint8Array is a valid skylink.
function validateSkylink(skylink: string | Uint8Array): Err {
// If the input is a string, convert it to a Uint8Array.
let skylinkU8: Uint8Array;
if (typeof skylink === "string") {
const [buf, err] = b64ToBuf(skylink);
if (err !== null) {
return addContextToErr(err, "unable to convert skylink from string");
}
skylinkU8 = buf;
} else {
skylinkU8 = skylink;
}
// skylink is now a Uint8
if (skylinkU8.length !== SKYLINK_U8_V1_V2_LENGTH) {
return `skylinkU8 has an invalid length: ${skylinkU8.length}`;
}
const [, , , errPSB] = parseSkylinkBitfield(skylinkU8);
if (errPSB !== null) {
return addContextToErr(errPSB, "skylink did not decode");
}
return null;
}
export { validateSkyfileMetadata, validateSkyfilePath, validateSkylink };