diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9bbf3d5b..69a3a6b7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -107,10 +107,8 @@ jobs: - run: npm run license-check - run: npm run lint - run: npm pack - - name: Setup BATS - uses: mig4/setup-bats@v1 - with: - bats-version: 1.2.1 + - name: Setup Bats and bats libs + uses: bats-core/bats-action@2.0.0 - run: bats bin/opentdf.bats - uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/roundtrip/encrypt-decrypt.sh b/.github/workflows/roundtrip/encrypt-decrypt.sh index fa94d7b4..ce21e14a 100755 --- a/.github/workflows/roundtrip/encrypt-decrypt.sh +++ b/.github/workflows/roundtrip/encrypt-decrypt.sh @@ -13,6 +13,7 @@ _nano_test() { echo "Hello World ${counter}" >"./${plain}" npx "$1" --log-level DEBUG \ --kasEndpoint http://localhost:65432/api/kas \ + --allowList http://localhost:65432 \ --oidcEndpoint http://localhost:65432/auth/realms/tdf \ --auth tdf-client:123-456 \ --output sample.txt.ntdf \ diff --git a/cli/bin/opentdf.bats b/cli/bin/opentdf.bats index fb3cb789..aa4c4360 100755 --- a/cli/bin/opentdf.bats +++ b/cli/bin/opentdf.bats @@ -2,22 +2,26 @@ @test "requires some arguments" { run $BATS_TEST_DIRNAME/opentdf.mjs + echo "$output" [[ $output == *"Not enough"* ]] } @test "requires optional arguments" { run $BATS_TEST_DIRNAME/opentdf.mjs encrypt noone + echo "$output" [[ $output == *"Missing required"* ]] } @test "fails with missing file arguments" { - run $BATS_TEST_DIRNAME/opentdf.mjs --kasEndpoint https://invalid --oidcEndpoint http://invalid --auth b:c encrypt notafile + run $BATS_TEST_DIRNAME/opentdf.mjs --kasEndpoint "https://example.com" --oidcEndpoint "http://invalid" --auth "b:c" encrypt [ "$status" -eq 1 ] - [[ $output == *"File is not accessable"* ]] + echo "$output" + [[ $output == *"Must specify file or pipe"* ]] } @test "version command" { run $BATS_TEST_DIRNAME/opentdf.mjs --version + echo "$output" [[ $output == *"@opentdf/client\":\""* ]] [[ $output == *"@opentdf/cli\":\""* ]] } diff --git a/cli/package-lock.json b/cli/package-lock.json index 76a0e705..18eda993 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -36,9 +36,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -371,7 +371,7 @@ "node_modules/@opentdf/client": { "version": "2.0.0", "resolved": "file:../lib/opentdf-client-2.0.0.tgz", - "integrity": "sha512-hbqa4laP/qPlrmsVoyNjW5Z55wBkbKXHsqQyyp90FtxmKCrUcuZhnLLQS4cBn7Wpx+8qa67UAcedQ2Xdb7Cn9w==", + "integrity": "sha512-AYYYJYSNJgUXNHJTK8pH+W74udFhwn1Co5/utjdfZZUFBjl2cGn+knfJe/Blrgj0S2VseOUs1nvwPiZYnAS/8w==", "dependencies": { "axios": "^1.6.1", "axios-retry": "^3.9.0", diff --git a/cli/src/cli.ts b/cli/src/cli.ts index 46e78f2b..74db3084 100644 --- a/cli/src/cli.ts +++ b/cli/src/cli.ts @@ -169,7 +169,7 @@ export const handleArgs = (args: string[]) => { // AUTH OPTIONS .option('kasEndpoint', { demandOption: true, - group: 'KAS Endpoint:', + group: 'KAS Configuration', type: 'string', description: 'URL to non-default KAS instance (https://mykas.net)', }) @@ -179,6 +179,12 @@ export const handleArgs = (args: string[]) => { type: 'string', description: 'URL to non-default OIDC IdP (https://myidp.net)', }) + .option('allowList', { + group: 'KAS Configuration', + desc: 'allowed KAS origins, comma separated; defaults to [kasEndpoint]', + type: 'string', + validate: (attributes: string) => attributes.split(','), + }) .option('auth', { group: 'Authentication:', type: 'string', @@ -286,13 +292,19 @@ export const handleArgs = (args: string[]) => { }, async (argv) => { log('DEBUG', 'Running decrypt command'); + const allowedKases = argv.allowList?.split(','); const authProvider = await processAuth(argv); log('DEBUG', `Initialized auth provider ${JSON.stringify(authProvider)}`); const kasEndpoint = argv.kasEndpoint; if (argv.containerType === 'tdf3') { log('DEBUG', `TDF3 Client`); - const client = new TDF3Client({ authProvider, kasEndpoint, dpopEnabled: argv.dpop }); + const client = new TDF3Client({ + allowedKases, + authProvider, + kasEndpoint, + dpopEnabled: argv.dpop, + }); log('SILLY', `Initialized client ${JSON.stringify(client)}`); log('DEBUG', `About to decrypt [${argv.file}]`); const ct = await client.decrypt(await tdf3DecryptParamsFor(argv)); @@ -306,8 +318,13 @@ export const handleArgs = (args: string[]) => { const dpopEnabled = !!argv.dpop; const client = argv.containerType === 'nano' - ? new NanoTDFClient({ authProvider, kasEndpoint, dpopEnabled }) - : new NanoTDFDatasetClient({ authProvider, kasEndpoint, dpopEnabled }); + ? new NanoTDFClient({ allowedKases, authProvider, kasEndpoint, dpopEnabled }) + : new NanoTDFDatasetClient({ + allowedKases, + authProvider, + kasEndpoint, + dpopEnabled, + }); const buffer = await processDataIn(argv.file as string); log('DEBUG', 'Decrypt data.'); @@ -359,10 +376,16 @@ export const handleArgs = (args: string[]) => { const authProvider = await processAuth(argv); log('DEBUG', `Initialized auth provider ${JSON.stringify(authProvider)}`); const kasEndpoint = argv.kasEndpoint; + const allowedKases = argv.allowList?.split(','); if ('tdf3' === argv.containerType) { log('DEBUG', `TDF3 Client`); - const client = new TDF3Client({ authProvider, kasEndpoint, dpopEnabled: argv.dpop }); + const client = new TDF3Client({ + allowedKases, + authProvider, + kasEndpoint, + dpopEnabled: argv.dpop, + }); log('SILLY', `Initialized client ${JSON.stringify(client)}`); const ct = await client.encrypt(await tdf3EncryptParamsFor(argv)); if (!ct) { @@ -378,8 +401,13 @@ export const handleArgs = (args: string[]) => { const dpopEnabled = !!argv.dpop; const client = argv.containerType === 'nano' - ? new NanoTDFClient({ authProvider, dpopEnabled, kasEndpoint }) - : new NanoTDFDatasetClient({ authProvider, dpopEnabled, kasEndpoint }); + ? new NanoTDFClient({ allowedKases, authProvider, dpopEnabled, kasEndpoint }) + : new NanoTDFDatasetClient({ + allowedKases, + authProvider, + dpopEnabled, + kasEndpoint, + }); log('SILLY', `Initialized client ${JSON.stringify(client)}`); addParams(client, argv); diff --git a/lib/src/access.ts b/lib/src/access.ts index ca8bee01..97899e24 100644 --- a/lib/src/access.ts +++ b/lib/src/access.ts @@ -1,5 +1,5 @@ import { type AuthProvider } from './auth/auth.js'; -import { pemToCryptoPublicKey } from './utils.js'; +import { pemToCryptoPublicKey, validateSecureUrl } from './utils.js'; export class RewrapRequest { signedRequestToken = ''; @@ -60,3 +60,23 @@ export async function fetchECKasPubKey(kasEndpoint: string): Promise const pem = await kasPubKeyResponse.json(); return pemToCryptoPublicKey(pem); } + +const origin = (u: string): string => { + try { + return new URL(u).origin; + } catch (e) { + console.log(`invalid kas url: [${u}]`); + throw e; + } +}; + +export class OriginAllowList { + origins: string[]; + constructor(urls: string[]) { + this.origins = urls.map(origin); + urls.forEach(validateSecureUrl); + } + allows(url: string): boolean { + return this.origins.includes(origin(url)); + } +} diff --git a/lib/src/nanotdf/Client.ts b/lib/src/nanotdf/Client.ts index 44760a87..dd9b85bd 100644 --- a/lib/src/nanotdf/Client.ts +++ b/lib/src/nanotdf/Client.ts @@ -3,16 +3,13 @@ import * as base64 from '../encodings/base64.js'; import { generateKeyPair, keyAgreement } from '../nanotdf-crypto/index.js'; import getHkdfSalt from './helpers/getHkdfSalt.js'; import DefaultParams from './models/DefaultParams.js'; -import { fetchWrappedKey } from '../access.js'; +import { fetchWrappedKey, OriginAllowList } from '../access.js'; import { AuthProvider, isAuthProvider, reqSignature } from '../auth/providers.js'; -import { - cryptoPublicToPem, - pemToCryptoPublicKey, - safeUrlCheck, - validateSecureUrl, -} from '../utils.js'; +import { UnsafeUrlError } from '../errors.js'; +import { cryptoPublicToPem, pemToCryptoPublicKey, validateSecureUrl } from '../utils.js'; export interface ClientConfig { + allowedKases?: string[]; authProvider: AuthProvider; dpopEnabled?: boolean; dpopKeys?: Promise; @@ -102,7 +99,7 @@ export default class Client { static readonly INITIAL_RELEASE_IV_SIZE = 3; static readonly IV_SIZE = 12; - allowedKases: string[]; + allowedKases: OriginAllowList; /* These variables are expected to be either assigned during initialization or within the methods. This is needed as the flow is very specific. Errors should be thrown if the necessary step is not completed. @@ -138,7 +135,7 @@ export default class Client { // TODO Disallow http KAS. For now just log as error validateSecureUrl(kasUrl); this.kasUrl = kasUrl; - this.allowedKases = [kasUrl]; + this.allowedKases = new OriginAllowList([kasUrl]); this.dpopEnabled = dpopEnabled; if (ephemeralKeyPair) { @@ -148,13 +145,13 @@ export default class Client { } this.iv = 1; } else { - const { authProvider, dpopEnabled, dpopKeys, ephemeralKeyPair, kasEndpoint } = + const { allowedKases, authProvider, dpopEnabled, dpopKeys, ephemeralKeyPair, kasEndpoint } = optsOrOldAuthProvider; this.authProvider = authProvider; // TODO Disallow http KAS. For now just log as error validateSecureUrl(kasEndpoint); this.kasUrl = kasEndpoint; - this.allowedKases = [kasEndpoint]; + this.allowedKases = new OriginAllowList(allowedKases || [kasEndpoint]); this.dpopEnabled = !!dpopEnabled; if (dpopKeys) { this.requestSignerKeyPair = dpopKeys; @@ -215,7 +212,9 @@ export default class Client { magicNumberVersion: TypedArray | ArrayBuffer, clientVersion: string ): Promise { - safeUrlCheck(this.allowedKases, kasRewrapUrl); + if (!this.allowedKases.allows(kasRewrapUrl)) { + throw new UnsafeUrlError(`request URL ∉ ${this.allowedKases.origins};`, kasRewrapUrl); + } // Ensure the ephemeral key pair has been set or generated (see createOidcServiceProvider) await this.fetchOIDCToken(); diff --git a/lib/src/utils.ts b/lib/src/utils.ts index 12873766..22e76cb8 100644 --- a/lib/src/utils.ts +++ b/lib/src/utils.ts @@ -1,5 +1,4 @@ import { type AxiosResponseHeaders, type RawAxiosResponseHeaders } from 'axios'; -import { UnsafeUrlError } from './errors.js'; import { base64 } from './encodings/index.js'; import { pemCertToCrypto, pemPublicToCrypto } from './nanotdf-crypto/index.js'; @@ -40,23 +39,6 @@ export function padSlashToUrl(u: string): string { return `${u}/`; } -const someStartsWith = (prefixes: string[], requestUrl: string): boolean => - prefixes.some((prixfixe) => requestUrl.startsWith(padSlashToUrl(prixfixe))); - -/** - * Checks that `testUrl` is prefixed with one of the given origin + path fragment URIs in urlPrefixes. - * - * Note this doesn't do anything special to queries or fragments and will fail to work properly if those are present on the prefixes - * @param urlPrefixes a list of origin parts of urls, possibly including some path fragment as well - * @param testUrl a url to see if it is prefixed by one or more of the `urlPrefixes` values - * @throws Error when testUrl is not present - */ -export const safeUrlCheck = (urlPrefixes: string[], testUrl: string): void | never => { - if (!someStartsWith(urlPrefixes, testUrl)) { - throw new UnsafeUrlError(`Invalid request URL: [${testUrl}] ∉ [${urlPrefixes}];`, testUrl); - } -}; - export function isBrowser() { return typeof window !== 'undefined'; // eslint-disable-line } diff --git a/lib/tdf3/src/client/index.ts b/lib/tdf3/src/client/index.ts index ed65256a..a25b3122 100644 --- a/lib/tdf3/src/client/index.ts +++ b/lib/tdf3/src/client/index.ts @@ -50,13 +50,14 @@ import { type DecryptSource, EncryptParamsBuilder, } from './builders.js'; -import * as defaultCryptoService from '../crypto/index.js'; -import { AttributeSet, Policy, SplitKey } from '../models/index.js'; +import { OriginAllowList } from '../../../src/access.js'; import { TdfError } from '../../../src/errors.js'; +import { EntityObject } from '../../../src/tdf/EntityObject.js'; import { Binary } from '../binary.js'; -import { EntityObject } from 'src/tdf/EntityObject.js'; import { AesGcmCipher } from '../ciphers/aes-gcm-cipher.js'; import { toCryptoKeyPair } from '../crypto/crypto-utils.js'; +import * as defaultCryptoService from '../crypto/index.js'; +import { AttributeSet, Policy, SplitKey } from '../models/index.js'; const GLOBAL_BYTE_LIMIT = 64 * 1000 * 1000 * 1000; // 64 GB, see WS-9363. const HTML_BYTE_LIMIT = 100 * 1000 * 1000; // 100 MB, see WS-9476. @@ -220,7 +221,7 @@ export class Client { * List of allowed KASes to connect to for rewrap requests. * Defaults to `[this.kasEndpoint]`. */ - readonly allowedKases: string[]; + readonly allowedKases: OriginAllowList; readonly kasKeys: Record> = {}; @@ -274,18 +275,17 @@ export class Client { const kasOrigin = new URL(this.kasEndpoint).origin; if (clientConfig.allowedKases) { - this.allowedKases = clientConfig.allowedKases.map((a) => new URL(a).origin); - if (!validateSecureUrl(this.kasEndpoint) && !this.allowedKases.includes(kasOrigin)) { + this.allowedKases = new OriginAllowList(clientConfig.allowedKases); + if (!validateSecureUrl(this.kasEndpoint) && !this.allowedKases.allows(kasOrigin)) { throw new TdfError(`Invalid KAS endpoint [${this.kasEndpoint}]`); } - this.allowedKases.forEach(validateSecureUrl); } else { if (!validateSecureUrl(this.kasEndpoint)) { throw new TdfError( `Invalid KAS endpoint [${this.kasEndpoint}]; to force, please list it among allowedKases` ); } - this.allowedKases = [kasOrigin]; + this.allowedKases = new OriginAllowList([kasOrigin]); } this.authProvider = config.authProvider; @@ -405,7 +405,7 @@ export class Client { ); const { keyForEncryption, keyForManifest } = await (keyMiddleware as EncryptKeyMiddleware)(); const ecfg: EncryptConfiguration = { - allowedKases: this.allowedKases, + allowList: this.allowedKases, attributeSet, byteLimit, cryptoService: this.cryptoService, @@ -482,7 +482,7 @@ export class Client { // TODO: Write error event to stream and don't await. return await (streamMiddleware as DecryptStreamMiddleware)( await readStream({ - allowedKases: this.allowedKases, + allowList: this.allowedKases, authProvider: this.authProvider, chunker, cryptoService: this.cryptoService, diff --git a/lib/tdf3/src/tdf.ts b/lib/tdf3/src/tdf.ts index acdad7d8..850598bc 100644 --- a/lib/tdf3/src/tdf.ts +++ b/lib/tdf3/src/tdf.ts @@ -33,6 +33,7 @@ import { concatUint8, } from './utils/index.js'; import { Binary } from './binary.js'; +import { OriginAllowList } from '../../src/access.js'; import { IllegalArgumentError, KasDecryptError, @@ -120,15 +121,11 @@ type Chunk = { _reject?: (value: unknown) => void; }; -export type TDFConfiguration = { - allowedKases?: string[]; - cryptoService: CryptoService; -}; - export type IntegrityAlgorithm = 'GMAC' | 'HS256'; export type EncryptConfiguration = { allowedKases?: string[]; + allowList?: OriginAllowList; cryptoService: CryptoService; dpopKeys: CryptoKeyPair; encryptionInformation: SplitKey; @@ -148,8 +145,8 @@ export type EncryptConfiguration = { }; export type DecryptConfiguration = { - // Normalized KAS origins to connect to - allowedKases: string[]; + allowedKases?: string[]; + allowList?: OriginAllowList; authProvider: AuthProvider | AppIdAuthProvider; cryptoService: CryptoService; entity?: EntityObject; @@ -163,7 +160,8 @@ export type DecryptConfiguration = { }; export type UpsertConfiguration = { - allowedKases: string[]; + allowedKases?: string[]; + allowList?: OriginAllowList; authProvider: AuthProvider | AppIdAuthProvider; entity?: EntityObject; @@ -467,12 +465,22 @@ function buildRequest(method: HttpMethod, url: string, body?: unknown): HttpRequ export async function upsert({ allowedKases, + allowList, authProvider, entity, privateKey, unsavedManifest, ignoreType, }: UpsertConfiguration): Promise { + const allowed = (() => { + if (allowList) { + return allowList; + } + if (!allowedKases) { + throw new Error('Upsert cannot be done without allowlist'); + } + return new OriginAllowList(allowedKases); + })(); const { keyAccess, policy } = unsavedManifest.encryptionInformation; const isAppIdProvider = authProvider && isAppIdProviderCheck(authProvider); if (authProvider === undefined) { @@ -486,7 +494,7 @@ export async function upsert({ return; } - if (!allowedKases.includes(keyAccessObject.url)) { + if (!allowed.allows(keyAccessObject.url)) { throw new KasUpsertError(`Unexpected KAS url: [${keyAccessObject.url}]`); } @@ -590,7 +598,8 @@ export async function writeStream(cfg: EncryptConfiguration): Promise> { - const origin = (u: string): string => new URL(u).origin; - const allowed = (k: KeyAccessObject) => allowedKases.includes(origin(k.url)); + const allowed = (k: KeyAccessObject) => allowedKases.allows(k.url); const splitIds = new Set(keyAccess.map(({ sid }) => sid ?? '')); const accessibleSplits = new Set(keyAccess.filter(allowed).map(({ sid }) => sid)); @@ -848,7 +856,7 @@ async function unwrapKey({ cryptoService, }: { manifest: Manifest; - allowedKases: string[]; + allowedKases: OriginAllowList; authProvider: AuthProvider | AppIdAuthProvider; dpopKeys: CryptoKeyPair; entity: EntityObject | undefined; @@ -1061,6 +1069,13 @@ export async function sliceAndDecrypt({ } export async function readStream(cfg: DecryptConfiguration) { + let { allowList } = cfg; + if (!allowList) { + if (!cfg.allowedKases) { + throw new Error('Upsert cannot be done without allowlist'); + } + allowList = new OriginAllowList(cfg.allowedKases); + } const { manifest, zipReader, centralDirectory } = await loadTDFStream(cfg.chunker); if (!manifest) { throw new Error('Missing manifest data'); @@ -1076,7 +1091,7 @@ export async function readStream(cfg: DecryptConfiguration) { const { metadata, reconstructedKeyBinary } = await unwrapKey({ manifest, authProvider: cfg.authProvider, - allowedKases: cfg.allowedKases, + allowedKases: allowList, dpopKeys: cfg.dpopKeys, entity: cfg.entity, cryptoService: cfg.cryptoService, diff --git a/lib/tests/mocha/unit/tdf.spec.ts b/lib/tests/mocha/unit/tdf.spec.ts index 64f9861e..47d35984 100644 --- a/lib/tests/mocha/unit/tdf.spec.ts +++ b/lib/tests/mocha/unit/tdf.spec.ts @@ -1,8 +1,9 @@ import { expect } from 'chai'; import * as TDF from '../../../tdf3/src/tdf.js'; +import { KeyAccessObject } from '../../../tdf3/src/models/key-access.js'; +import { OriginAllowList } from '../../../src/access.js'; import { KasDecryptError, TdfError } from '../../../src/errors.js'; -import { KeyAccessObject } from 'tdf3/src/models/key-access.js'; const sampleCert = ` -----BEGIN CERTIFICATE----- @@ -100,7 +101,7 @@ describe('splitLookupTableFactory', () => { { sid: 'split1', type: 'remote', url: 'https://kas1', protocol: 'kas' }, { sid: 'split2', type: 'remote', url: 'https://kas2', protocol: 'kas' }, ]; - const allowedKases = ['https://kas1', 'https://kas2']; + const allowedKases = new OriginAllowList(['https://kas1', 'https://kas2']); const result = TDF.splitLookupTableFactory(keyAccess, allowedKases); @@ -115,7 +116,7 @@ describe('splitLookupTableFactory', () => { { sid: 'split1', type: 'remote', url: 'https://kas1', protocol: 'kas' }, { sid: 'split2', type: 'remote', url: 'https://kas3', protocol: 'kas' }, // kas3 is not allowed ]; - const allowedKases = ['https://kas1']; + const allowedKases = new OriginAllowList(['https://kas1']); expect(() => TDF.splitLookupTableFactory(keyAccess, allowedKases)).to.throw( KasDecryptError, @@ -128,7 +129,7 @@ describe('splitLookupTableFactory', () => { { sid: 'split1', type: 'remote', url: 'https://kas1', protocol: 'kas' }, { sid: 'split1', type: 'remote', url: 'https://kas1', protocol: 'kas' }, // duplicate URL in same splitId ]; - const allowedKases = ['https://kas1']; + const allowedKases = new OriginAllowList(['https://kas1']); expect(() => TDF.splitLookupTableFactory(keyAccess, allowedKases)).to.throw( KasDecryptError, @@ -138,7 +139,7 @@ describe('splitLookupTableFactory', () => { it('should handle empty keyAccess array', () => { const keyAccess: KeyAccessObject[] = []; - const allowedKases: string[] = []; + const allowedKases = new OriginAllowList([]); const result = TDF.splitLookupTableFactory(keyAccess, allowedKases); @@ -149,7 +150,7 @@ describe('splitLookupTableFactory', () => { const keyAccess: KeyAccessObject[] = [ { sid: 'split1', type: 'remote', url: 'https://kas1', protocol: 'kas' }, ]; - const allowedKases: string[] = []; + const allowedKases = new OriginAllowList([]); expect(() => TDF.splitLookupTableFactory(keyAccess, allowedKases)).to.throw( KasDecryptError, @@ -163,7 +164,7 @@ describe('splitLookupTableFactory', () => { ]; const allowedKases = ['https://kas1']; - const result = TDF.splitLookupTableFactory(keyAccess, allowedKases); + const result = TDF.splitLookupTableFactory(keyAccess, new OriginAllowList(allowedKases)); expect(result).to.deep.equal({ '': { 'https://kas1': keyAccess[0] }, diff --git a/lib/tests/web/utils.test.ts b/lib/tests/web/utils.test.ts index 26f674ce..6791f4be 100644 --- a/lib/tests/web/utils.test.ts +++ b/lib/tests/web/utils.test.ts @@ -7,7 +7,6 @@ import { estimateSkewFromHeaders, padSlashToUrl, rstrip, - safeUrlCheck, validateSecureUrl, } from '../../src/utils.js'; @@ -95,48 +94,6 @@ describe('padSlashToUrl', () => { }); }); -describe('safeUrlCheck', () => { - it('some checks', () => { - expect(() => safeUrlCheck([], 'https://my.xyz/somewhere/else')).to.throw('Invalid request URL'); - expect(() => safeUrlCheck([''], 'https://my.xyz/somewhere/else')).to.throw( - 'Invalid request URL' - ); - expect(() => safeUrlCheck(['https://my.xyz'], 'https://my.xyz/somewhere')).to.not.throw( - 'Invalid request URL' - ); - expect(() => safeUrlCheck(['https://my.xyz'], 'https://my.xyz.com/somewhere/else')).to.throw( - 'Invalid request URL' - ); - expect(() => safeUrlCheck(['https://my.xyz'], 'http://my.xyz/somewhere/else')).to.throw( - 'Invalid request URL' - ); - expect(() => - safeUrlCheck(['https://my.xyz/somewhere'], 'https://my.xyz/somewhere/else') - ).to.not.throw('Invalid request URL'); - expect(() => - safeUrlCheck( - ['https://your.place', 'https://my.xyz/somewhere'], - 'https://my.xyz/somewhere/else' - ) - ).to.not.throw('Invalid request URL'); - expect(() => - safeUrlCheck(['https://my.xyz/somewhere'], 'https://my.xyz/somewhereelse/') - ).to.throw('Invalid request URL'); - expect(() => safeUrlCheck(['https://my.xyz/somewhere'], 'https://my.xyz/elsewhere/')).to.throw( - 'Invalid request URL' - ); - }); - - it('returns invalid url', () => { - try { - safeUrlCheck([], 'https://my.xyz/somewhere/else'); - expect.fail(); - } catch (e) { - expect(e).to.have.property('url', 'https://my.xyz/somewhere/else'); - } - }); -}); - function mockApiResponse(date = '', status = 200) { return new globalThis.Response(`{}`, { status, diff --git a/remote-store/package-lock.json b/remote-store/package-lock.json index 8fd068bd..3668d51f 100644 --- a/remote-store/package-lock.json +++ b/remote-store/package-lock.json @@ -1536,9 +1536,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1820,7 +1820,7 @@ "node_modules/@opentdf/client": { "version": "2.0.0", "resolved": "file:../lib/opentdf-client-2.0.0.tgz", - "integrity": "sha512-hbqa4laP/qPlrmsVoyNjW5Z55wBkbKXHsqQyyp90FtxmKCrUcuZhnLLQS4cBn7Wpx+8qa67UAcedQ2Xdb7Cn9w==", + "integrity": "sha512-AYYYJYSNJgUXNHJTK8pH+W74udFhwn1Co5/utjdfZZUFBjl2cGn+knfJe/Blrgj0S2VseOUs1nvwPiZYnAS/8w==", "dependencies": { "axios": "^1.6.1", "axios-retry": "^3.9.0", diff --git a/web-app/package-lock.json b/web-app/package-lock.json index 5c0280da..21217086 100644 --- a/web-app/package-lock.json +++ b/web-app/package-lock.json @@ -350,9 +350,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -606,7 +606,7 @@ "node_modules/@opentdf/client": { "version": "2.0.0", "resolved": "file:../lib/opentdf-client-2.0.0.tgz", - "integrity": "sha512-hbqa4laP/qPlrmsVoyNjW5Z55wBkbKXHsqQyyp90FtxmKCrUcuZhnLLQS4cBn7Wpx+8qa67UAcedQ2Xdb7Cn9w==", + "integrity": "sha512-AYYYJYSNJgUXNHJTK8pH+W74udFhwn1Co5/utjdfZZUFBjl2cGn+knfJe/Blrgj0S2VseOUs1nvwPiZYnAS/8w==", "dependencies": { "axios": "^1.6.1", "axios-retry": "^3.9.0", @@ -3930,9 +3930,9 @@ } }, "@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "requires": { "regenerator-runtime": "^0.14.0" } @@ -4090,7 +4090,7 @@ }, "@opentdf/client": { "version": "file:../lib/opentdf-client-2.0.0.tgz", - "integrity": "sha512-hbqa4laP/qPlrmsVoyNjW5Z55wBkbKXHsqQyyp90FtxmKCrUcuZhnLLQS4cBn7Wpx+8qa67UAcedQ2Xdb7Cn9w==", + "integrity": "sha512-AYYYJYSNJgUXNHJTK8pH+W74udFhwn1Co5/utjdfZZUFBjl2cGn+knfJe/Blrgj0S2VseOUs1nvwPiZYnAS/8w==", "requires": { "axios": "^1.6.1", "axios-retry": "^3.9.0",