diff --git a/lib/package.json b/lib/package.json index 806c0410..f1c22aae 100644 --- a/lib/package.json +++ b/lib/package.json @@ -49,9 +49,9 @@ "license-check": "license-checker-rseidelsohn --production --onlyAllow 'Apache-2.0; BSD; CC-BY-4.0; ISC; MIT'", "lint": "eslint ./src/**/*.ts ./tdf3/**/*.ts ./tests/**/*.ts", "prepack": "npm run build", - "test": "npm run build && npm run test:wtr && npm run test:mocha && npm run test:browser && npm run coverage:merge", + "test": "npm run build && npm run test:mocha && npm run test:wtr && npm run test:browser && npm run coverage:merge", "test:browser": "rm -rf tests/mocha/dist && node tests/server.cjs & npx webpack --config webpack.test.config.cjs && npx karma start karma.conf.cjs; node tests/stopServer.cjs", - "test:mocha": "node tests/server.cjs & c8 --exclude=\"dist/web/tests/\" --exclude=\"dist/web/tdf3/src/utils/aws-lib-storage/\" --exclude=\"dist/web/tests/**/*\" --report-dir=./coverage/mocha mocha 'dist/web/tests/mocha/**/*.spec.js' --file tests/mocha/setup.js ; npx c8 report --reporter=json --report-dir=./coverage/mocha; node tests/stopServer.cjs", + "test:mocha": "node tests/server.cjs & trap \"node tests/stopServer.cjs\" EXIT; c8 --exclude=\"dist/web/tests/\" --exclude=\"dist/web/tdf3/src/utils/aws-lib-storage/\" --exclude=\"dist/web/tests/**/*\" --report-dir=./coverage/mocha mocha 'dist/web/tests/mocha/**/*.spec.js' --file tests/mocha/setup.js && npx c8 report --reporter=json --report-dir=./coverage/mocha", "test:wtr": "web-test-runner", "watch": "(trap 'kill 0' SIGINT; npm run build && (npm run build:watch & npm run test -- --watch))" }, diff --git a/lib/tdf3/src/models/attribute-set.ts b/lib/tdf3/src/models/attribute-set.ts index b1faeedf..f01fa292 100644 --- a/lib/tdf3/src/models/attribute-set.ts +++ b/lib/tdf3/src/models/attribute-set.ts @@ -27,7 +27,15 @@ const ATTRIBUTE_OBJECT_SCHEMA: JSONSchemaType = { additionalProperties: false, }; -const validator = new Ajv.default(); +const validator = (() => { + try { + //@ts-expect-error: Ajv not a constructor + return new Ajv(); + } catch (e) { + console.log(e); + } + return new Ajv.default(); +})(); export class AttributeSet { attributes: AttributeObject[]; @@ -148,11 +156,13 @@ export class AttributeSet { * @return {Object} - Decrypted and added attribute object */ addJwtAttribute(jwtAttribute: { jwt: string }) { - const { jwt: attrJwt } = jwtAttribute; + const attrJwt = jwtAttribute?.jwt; // Can't verify the JWT because the client does not have the easPublicKey, // but the contents of the JWT can be decoded. - const attrObjPayload = decodeJwt(attrJwt); - if (!attrObjPayload) return null; + const attrObjPayload = attrJwt && decodeJwt(attrJwt); + if (!attrObjPayload) { + return null; + } // JWT payloads contain many things, incluing .iat and .exp. This // extraneous material should be stripped away before adding the // attribute to the attributeSet. diff --git a/lib/tests/mocha/encrypt-decrypt.spec.ts b/lib/tests/mocha/encrypt-decrypt.spec.ts index eaa99b08..518dbb72 100644 --- a/lib/tests/mocha/encrypt-decrypt.spec.ts +++ b/lib/tests/mocha/encrypt-decrypt.spec.ts @@ -18,58 +18,61 @@ describe('encrypt decrypt test', async function () { const kasUrl = `http://localhost:4000/`; const kasPublicKey = await TDF.extractPemFromKeyString(Mocks.kasPublicKey); - const sandbox = createSandbox(); - const tdf1 = TDF.create({ cryptoService }); - sandbox.replace( - TDF, - 'create', - sandbox.fake(() => tdf1) - ); - sandbox.spy(tdf1, '_generateManifest'); - sandbox.stub(tdf1, 'unwrapKey').callsFake(async () => { - // @ts-ignore - const keyInfo = tdf1._generateManifest.lastCall.args[0]; - return { - reconstructedKeyBinary: keyInfo.unwrappedKeyBinary as Binary, - metadata: undefined, - }; - }); - it('encrypt-decrypt stream source happy path', async function () { - const client = new Client.Client({ - kasEndpoint: kasUrl, - kasPublicKey, - keypair: { publicKey: Mocks.entityPublicKey, privateKey: Mocks.entityPrivateKey }, - clientId: 'id', - authProvider, - }); + const sandbox = createSandbox(); + try { + const tdf1 = TDF.create({ cryptoService }); + sandbox.replace( + TDF, + 'create', + sandbox.fake(() => tdf1) + ); + sandbox.spy(tdf1, '_generateManifest'); + sandbox.stub(tdf1, 'unwrapKey').callsFake(async () => { + // @ts-ignore + const keyInfo = tdf1._generateManifest.lastCall.args[0]; + return { + reconstructedKeyBinary: keyInfo.unwrappedKeyBinary as Binary, + metadata: undefined, + }; + }); + const client = new Client.Client({ + kasEndpoint: kasUrl, + kasPublicKey, + keypair: { publicKey: Mocks.entityPublicKey, privateKey: Mocks.entityPrivateKey }, + clientId: 'id', + authProvider, + }); - const eo = await Mocks.getEntityObject(); - const scope = Mocks.getScope(); + const eo = await Mocks.getEntityObject(); + const scope = Mocks.getScope(); - const encryptedStream = await client.encrypt({ - eo, - metadata: Mocks.getMetadataObject(), - offline: true, - scope, - source: new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(expectedVal)); - controller.close(); - }, - }), - }); + const encryptedStream = await client.encrypt({ + eo, + metadata: Mocks.getMetadataObject(), + offline: true, + scope, + source: new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(expectedVal)); + controller.close(); + }, + }), + }); - const decryptStream = await client.decrypt({ - eo, - source: { - type: 'stream', - location: encryptedStream.stream, - }, - }); + const decryptStream = await client.decrypt({ + eo, + source: { + type: 'stream', + location: encryptedStream.stream, + }, + }); - const { value: decryptedText } = await decryptStream.stream.getReader().read(); + const { value: decryptedText } = await decryptStream.stream.getReader().read(); - assert.equal(new TextDecoder().decode(decryptedText), expectedVal); + assert.equal(new TextDecoder().decode(decryptedText), expectedVal); + } finally { + sandbox.restore(); + } }); }); diff --git a/lib/tests/mocha/unit/attribute-set.spec.ts b/lib/tests/mocha/unit/attribute-set.spec.ts index 4369c423..87a24d59 100644 --- a/lib/tests/mocha/unit/attribute-set.spec.ts +++ b/lib/tests/mocha/unit/attribute-set.spec.ts @@ -209,4 +209,22 @@ describe('AttributeSet', function () { const aSet = new AttributeSet(); assert.deepEqual(aSet.get('unknown URL'), null); }); + + it('empty jwt attribute', async () => { + const aSet = new AttributeSet(); + // @ts-expect-error invalid value + aSet.addJwtAttribute({}); + assert.equal(aSet.attributes.length, 0); + assert.deepEqual(aSet.getDefault(), null); + }); + + it('simplest jwt attribute', async () => { + const aSet = new AttributeSet(); + const attribute = 'https://example.com/attr/test-case/value/bar'; + const jwtAttr = await Mocks.createJwtAttribute({ attribute }); + aSet.addJwtAttribute(jwtAttr); + + const expected = Mocks.createAttribute({ attribute }); + assert.include(aSet.get(attribute), expected); + }); }); diff --git a/lib/tests/mocha/unit/tdf.spec.ts b/lib/tests/mocha/unit/tdf.spec.ts index cbbb3b1d..17a545f3 100644 --- a/lib/tests/mocha/unit/tdf.spec.ts +++ b/lib/tests/mocha/unit/tdf.spec.ts @@ -51,7 +51,8 @@ describe('TDF', () => { }); it('allowedKases', () => { - const actual = TDF.create({ allowedKases: ['https://local.virtru.com'], cryptoService }); + const cfg = { allowedKases: ['https://local.virtru.com'], cryptoService }; + const actual = TDF.create(cfg); expect(actual.allowedKases).to.contain('https://local.virtru.com'); });