From ca4caeb2124ef39f73749bc7b4613611b6fdf551 Mon Sep 17 00:00:00 2001 From: Josh Floth Date: Wed, 27 Sep 2023 00:28:03 -0500 Subject: [PATCH 1/4] move conditional property validation to javascript for protocol action rules --- README.md | 2 +- .../interface-methods/protocol-rule-set.json | 24 +------- src/core/dwn-error.ts | 1 + src/interfaces/protocols-configure.ts | 23 +++++++- tests/interfaces/protocols-configure.spec.ts | 57 +++++++++++++++++++ .../protocols/protocols-configure.spec.ts | 28 --------- 6 files changed, 80 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 385a7a4ce..ea2584311 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Here's to a thrilling Hacktoberfest voyage with us! 🎉 # Decentralized Web Node (DWN) SDK Code Coverage -![Statements](https://img.shields.io/badge/statements-97.77%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95.04%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.28%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.77%25-brightgreen.svg?style=flat) +![Statements](https://img.shields.io/badge/statements-97.72%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-94.99%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.28%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.72%25-brightgreen.svg?style=flat) - [Introduction](#introduction) - [Installation](#installation) diff --git a/json-schemas/interface-methods/protocol-rule-set.json b/json-schemas/interface-methods/protocol-rule-set.json index 343439dc7..791abf704 100644 --- a/json-schemas/interface-methods/protocol-rule-set.json +++ b/json-schemas/interface-methods/protocol-rule-set.json @@ -32,29 +32,7 @@ "who": { "type": "string", "enum": [ - "anyone" - ] - }, - "can": { - "type": "string", - "enum": [ - "read", - "write" - ] - } - } - }, - { - "required": [ - "who", - "of", - "can" - ], - "additionalProperties": false, - "properties": { - "who": { - "type": "string", - "enum": [ + "anyone", "author", "recipient" ] diff --git a/src/core/dwn-error.ts b/src/core/dwn-error.ts index 73e25a66a..025d9b6c7 100644 --- a/src/core/dwn-error.ts +++ b/src/core/dwn-error.ts @@ -45,6 +45,7 @@ export enum DwnErrorCode { ProtocolAuthorizationNotARole = 'ProtocolAuthorizationNotARole', ProtocolsConfigureGlobalRoleAtProhibitedProtocolPath = 'ProtocolsConfigureGlobalRoleAtProhibitedProtocolPath', ProtocolsConfigureInvalidRole = 'ProtocolsConfigureInvalidRole', + ProtocolsConfigureInvalidAction = 'ProtocolsConfigureInvalidAction', ProtocolsConfigureUnauthorized = 'ProtocolsConfigureUnauthorized', ProtocolsQueryUnauthorized = 'ProtocolsQueryUnauthorized', RecordsDecryptNoMatchingKeyEncryptedFound = 'RecordsDecryptNoMatchingKeyEncryptedFound', diff --git a/src/interfaces/protocols-configure.ts b/src/interfaces/protocols-configure.ts index fa6e195bd..00da71f8c 100644 --- a/src/interfaces/protocols-configure.ts +++ b/src/interfaces/protocols-configure.ts @@ -8,8 +8,8 @@ import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; import { normalizeProtocolUrl, normalizeSchemaUrl, validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../utils/url.js'; export type ProtocolsConfigureOptions = { - messageTimestamp? : string; - definition : ProtocolDefinition; + messageTimestamp?: string; + definition: ProtocolDefinition; authorizationSigner: Signer; permissionsGrantId?: string; }; @@ -92,15 +92,32 @@ export class ProtocolsConfigure extends Message { ); } - // Validate that all `role` properties contain protocol paths $globalRole records + // Validate $actions in the ruleset const actions = ruleSet.$actions ?? []; for (const action of actions) { + // Validate that all `role` properties contain protocol paths $globalRole records if (action.role !== undefined && !globalRoles.includes(action.role)) { throw new DwnError( DwnErrorCode.ProtocolsConfigureInvalidRole, `Invalid role '${action.role}' found at protocol path '${protocolPath}'` ); } + + // Validate that if `who` is set to `anyone` then `of` is not set + if (action.who === 'anyone' && action.of) { + throw new DwnError( + DwnErrorCode.ProtocolsConfigureInvalidAction, + `'of' is not allowed at protocol path (${protocolPath}) when 'who' is 'anyone'.` + ); + } + + // Validate that if `who` is not set to `anyone` then `of` is set + if (action.who !== undefined && ['author', 'recipient'].includes(action.who) && !action.of) { + throw new DwnError( + DwnErrorCode.ProtocolsConfigureInvalidAction, + `'of' is required at protocol path (${protocolPath}) when 'who' is 'author' or 'recipient'.` + ); + } } // Validate nested rule sets diff --git a/tests/interfaces/protocols-configure.spec.ts b/tests/interfaces/protocols-configure.spec.ts index 17f2bc0b7..3a0f5f84f 100644 --- a/tests/interfaces/protocols-configure.spec.ts +++ b/tests/interfaces/protocols-configure.spec.ts @@ -165,6 +165,63 @@ describe('ProtocolsConfigure', () => { await expect(createProtocolsConfigurePromise) .to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidRole); }); + + it('rejects protocol definitions with actions that contain `of` and `who` is `anyone`', async () => { + const definition = { + published : true, + protocol : 'http://example.com', + types : { + message: {}, + }, + structure: { + message: { + $actions: [{ + who : 'author', // Not a valid role + can : 'read' + }] + } + } + }; + + const alice = await TestDataGenerator.generatePersona(); + + const createProtocolsConfigurePromise = ProtocolsConfigure.create({ + authorizationSigner: Jws.createSigner(alice), + definition + }); + + await expect(createProtocolsConfigurePromise) + .to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidAction); + }); + + it('rejects protocol definitions with actions that don\'t contain `of` and `who` is `author` or `recipient`', async () => { + const definition = { + published : true, + protocol : 'http://example.com', + types : { + message: {}, + }, + structure: { + message: { + $actions: [{ + who : 'author', // Not a valid role + can : 'read' + }] + } + } + }; + + const alice = await TestDataGenerator.generatePersona(); + + const createProtocolsConfigurePromise = ProtocolsConfigure.create({ + authorizationSigner: Jws.createSigner(alice), + definition + }); + + await expect(createProtocolsConfigurePromise) + .to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidAction); + }); + }); }); }); diff --git a/tests/validation/json-schemas/protocols/protocols-configure.spec.ts b/tests/validation/json-schemas/protocols/protocols-configure.spec.ts index 8aa662711..dd28ee558 100644 --- a/tests/validation/json-schemas/protocols/protocols-configure.spec.ts +++ b/tests/validation/json-schemas/protocols/protocols-configure.spec.ts @@ -72,33 +72,5 @@ describe('ProtocolsConfigure schema definition', () => { }).throws('/$actions/0'); } }); - - it('#183 - should throw if required `of` is missing in rule-set', async () => { - const invalidRuleSet = { - $actions: [{ - who : 'author', - // of: 'thread', // intentionally missing - can : 'read' - }] - }; - - expect(() => { - validateJsonSchema('ProtocolRuleSet', invalidRuleSet); - }).throws('/$actions/0'); - }); - - it('#183 - should throw if `of` is present in `anyone` rule-set', async () => { - const invalidRuleSet = { - $actions: [{ - who : 'anyone', - of : 'thread', // intentionally present - can : 'read' - }] - }; - - expect(() => { - validateJsonSchema('ProtocolRuleSet', invalidRuleSet); - }).throws('/$actions/0'); - }); }); }); From 453207033c63dbfc8a5cfc02b6709b77bb225462 Mon Sep 17 00:00:00 2001 From: Josh Floth Date: Wed, 27 Sep 2023 01:12:24 -0500 Subject: [PATCH 2/4] fix tests --- README.md | 2 +- src/interfaces/protocols-configure.ts | 4 ++-- tests/interfaces/protocols-configure.spec.ts | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ea2584311..a1ba1ed5c 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Here's to a thrilling Hacktoberfest voyage with us! 🎉 # Decentralized Web Node (DWN) SDK Code Coverage -![Statements](https://img.shields.io/badge/statements-97.72%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-94.99%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.28%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.72%25-brightgreen.svg?style=flat) +![Statements](https://img.shields.io/badge/statements-97.78%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95.06%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.28%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.78%25-brightgreen.svg?style=flat) - [Introduction](#introduction) - [Installation](#installation) diff --git a/src/interfaces/protocols-configure.ts b/src/interfaces/protocols-configure.ts index 00da71f8c..dbdf81235 100644 --- a/src/interfaces/protocols-configure.ts +++ b/src/interfaces/protocols-configure.ts @@ -107,7 +107,7 @@ export class ProtocolsConfigure extends Message { if (action.who === 'anyone' && action.of) { throw new DwnError( DwnErrorCode.ProtocolsConfigureInvalidAction, - `'of' is not allowed at protocol path (${protocolPath}) when 'who' is 'anyone'.` + `'of' is not allowed at protocol path (${protocolPath})` ); } @@ -115,7 +115,7 @@ export class ProtocolsConfigure extends Message { if (action.who !== undefined && ['author', 'recipient'].includes(action.who) && !action.of) { throw new DwnError( DwnErrorCode.ProtocolsConfigureInvalidAction, - `'of' is required at protocol path (${protocolPath}) when 'who' is 'author' or 'recipient'.` + `'of' is required at protocol path (${protocolPath})` ); } } diff --git a/tests/interfaces/protocols-configure.spec.ts b/tests/interfaces/protocols-configure.spec.ts index 3a0f5f84f..b800cc8dd 100644 --- a/tests/interfaces/protocols-configure.spec.ts +++ b/tests/interfaces/protocols-configure.spec.ts @@ -176,7 +176,8 @@ describe('ProtocolsConfigure', () => { structure: { message: { $actions: [{ - who : 'author', // Not a valid role + who : 'anyone', + of : 'message', // Not allowed can : 'read' }] } @@ -204,7 +205,8 @@ describe('ProtocolsConfigure', () => { structure: { message: { $actions: [{ - who : 'author', // Not a valid role + who : 'author', + // of : 'message', // Intentionally missing can : 'read' }] } From 83792a9704d3b489953fcb39fda68f977385ea59 Mon Sep 17 00:00:00 2001 From: Henry Tsai Date: Thu, 28 Sep 2023 10:18:36 -0700 Subject: [PATCH 3/4] replaced reuse of error code --- src/core/dwn-error.ts | 3 ++- src/interfaces/protocols-configure.ts | 4 ++-- tests/interfaces/protocols-configure.spec.ts | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/dwn-error.ts b/src/core/dwn-error.ts index 025d9b6c7..07e80043f 100644 --- a/src/core/dwn-error.ts +++ b/src/core/dwn-error.ts @@ -45,7 +45,8 @@ export enum DwnErrorCode { ProtocolAuthorizationNotARole = 'ProtocolAuthorizationNotARole', ProtocolsConfigureGlobalRoleAtProhibitedProtocolPath = 'ProtocolsConfigureGlobalRoleAtProhibitedProtocolPath', ProtocolsConfigureInvalidRole = 'ProtocolsConfigureInvalidRole', - ProtocolsConfigureInvalidAction = 'ProtocolsConfigureInvalidAction', + ProtocolsConfigureInvalidActionMissingOf = 'ProtocolsConfigureInvalidActionMissingOf', + ProtocolsConfigureInvalidActionOfNotAllowed = 'ProtocolsConfigureInvalidActionOfNotAllowed', ProtocolsConfigureUnauthorized = 'ProtocolsConfigureUnauthorized', ProtocolsQueryUnauthorized = 'ProtocolsQueryUnauthorized', RecordsDecryptNoMatchingKeyEncryptedFound = 'RecordsDecryptNoMatchingKeyEncryptedFound', diff --git a/src/interfaces/protocols-configure.ts b/src/interfaces/protocols-configure.ts index dbdf81235..bf21614c8 100644 --- a/src/interfaces/protocols-configure.ts +++ b/src/interfaces/protocols-configure.ts @@ -106,7 +106,7 @@ export class ProtocolsConfigure extends Message { // Validate that if `who` is set to `anyone` then `of` is not set if (action.who === 'anyone' && action.of) { throw new DwnError( - DwnErrorCode.ProtocolsConfigureInvalidAction, + DwnErrorCode.ProtocolsConfigureInvalidActionOfNotAllowed, `'of' is not allowed at protocol path (${protocolPath})` ); } @@ -114,7 +114,7 @@ export class ProtocolsConfigure extends Message { // Validate that if `who` is not set to `anyone` then `of` is set if (action.who !== undefined && ['author', 'recipient'].includes(action.who) && !action.of) { throw new DwnError( - DwnErrorCode.ProtocolsConfigureInvalidAction, + DwnErrorCode.ProtocolsConfigureInvalidActionMissingOf, `'of' is required at protocol path (${protocolPath})` ); } diff --git a/tests/interfaces/protocols-configure.spec.ts b/tests/interfaces/protocols-configure.spec.ts index b800cc8dd..84b2df01c 100644 --- a/tests/interfaces/protocols-configure.spec.ts +++ b/tests/interfaces/protocols-configure.spec.ts @@ -192,7 +192,7 @@ describe('ProtocolsConfigure', () => { }); await expect(createProtocolsConfigurePromise) - .to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidAction); + .to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidActionOfNotAllowed); }); it('rejects protocol definitions with actions that don\'t contain `of` and `who` is `author` or `recipient`', async () => { @@ -221,7 +221,7 @@ describe('ProtocolsConfigure', () => { }); await expect(createProtocolsConfigurePromise) - .to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidAction); + .to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidActionMissingOf); }); }); From 3ffb7280352b3b1a1a56dfbc7e59ef60149602ea Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Thu, 28 Sep 2023 10:29:56 -0700 Subject: [PATCH 4/4] npm audit fix get-func-name (#521) --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4a9cc289..764360b0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3166,9 +3166,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*"