New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@ucanto/validator

Package Overview
Dependencies
Maintainers
3
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ucanto/validator - npm Package Compare versions

Comparing version 4.0.2 to 4.0.3

3

dist/src/capability.d.ts

@@ -44,3 +44,3 @@ export function capability<A extends API.Ability, R extends API.URI<`${string}:`>, C extends API.Caveats = {}>(descriptor: API.Descriptor<A, R, C>): API.TheCapabilityParser<API.CapabilityMatch<A, R, C>>;

unknown: API.Capability<API.Ability, `${string}:${string}`, unknown>[];
errors: (API.DelegationError | MatchError)[];
errors: (MatchError | API.DelegationError)[];
matches: (DerivedMatch<T, M> | M)[];

@@ -50,3 +50,4 @@ };

}
import { DelegationError as MatchError } from "./error.js";
export {};
//# sourceMappingURL=capability.d.ts.map

@@ -42,3 +42,3 @@ /**

name: "InvalidClaim";
causes: (API.InvalidCapability | API.DelegationError | API.EscalatedDelegation)[];
causes: (API.InvalidCapability | API.EscalatedDelegation | API.DelegationError)[];
context: object;

@@ -48,3 +48,3 @@ /**

*/
get cause(): API.InvalidCapability | API.DelegationError | API.EscalatedDelegation;
get cause(): API.InvalidCapability | API.EscalatedDelegation | API.DelegationError;
}

@@ -77,2 +77,12 @@ /**

}
export class DIDKeyResolutionError extends Failure {
/**
* @param {API.UCAN.DID} did
* @param {API.Unauthorized} [cause]
*/
constructor(did: API.UCAN.DID, cause?: API.Unauthorized | undefined);
name: "DIDKeyResolutionError";
did: `did:${string}:${string}`;
cause: API.Unauthorized | undefined;
}
/**

@@ -154,2 +164,9 @@ * @implements {API.InvalidAudience}

get validAt(): number;
toJSON(): {
error: true;
name: "NotValidBefore";
message: string;
validAt: number;
stack: string | undefined;
};
}

@@ -156,0 +173,0 @@ export function indent(message: string, indent?: string): string;

export { capability } from "./capability.js";
export * from "./schema.js";
export * as Schema from "./schema.js";
export function access<A extends API.Ability, R extends API.URI<`${string}:`>, URI extends R, C extends API.Caveats>(invocation: API.Invocation<API.Capability<A, URI, API.InferCaveats<C>>>, { canIssue, principal, resolve, capability }: API.ValidationOptions<API.ParsedCapability<A, R, API.InferCaveats<C>>>): Promise<API.Result<Authorization<API.ParsedCapability<A, R, API.InferCaveats<C>>>, API.Unauthorized>>;
export function authorize<C extends {
can: API.Ability;
with: API.URI<`${string}:`>;
nb?: {} | undefined;
}, Match extends API.Match<any, API.UnknownMatch>>(match: Match, config: Required<API.ValidationOptions<C>>): Promise<API.Result<Authorization<API.ParsedCapability>, API.InvalidClaim>>;
export function access<A extends API.Ability, R extends API.URI<`${string}:`>, URI extends R, C extends API.Caveats>(invocation: API.Invocation<API.Capability<A, URI, API.InferCaveats<C>>>, { capability, ...config }: API.ValidationOptions<API.ParsedCapability<A, R, API.InferCaveats<C>>>): Promise<API.Result<Authorization<API.ParsedCapability<A, R, API.InferCaveats<C>>>, API.Unauthorized>>;
export function claim<A extends API.Ability, R extends API.URI<`${string}:`>, C extends API.Caveats>(capability: API.CapabilityParser<API.Match<API.ParsedCapability<A, R, API.InferCaveats<C>>, API.UnknownMatch>>, proofs: API.Proof[], { authority, principal, resolveDIDKey, canIssue, resolve, }: API.ClaimOptions): Promise<API.Result<Authorization<API.ParsedCapability<A, R, API.InferCaveats<C>>>, API.Unauthorized>>;
export function authorize<Match extends API.Match<any, API.UnknownMatch>>(match: Match, config: Required<API.ClaimOptions>): Promise<API.Result<Authorization<API.ParsedCapability>, API.InvalidClaim>>;
import { Failure } from "./error.js";
import { UnavailableProof } from "./error.js";
import { MalformedCapability } from "./error.js";
import { DIDKeyResolutionError } from "./error.js";
import * as Schema from "./schema.js";
import * as API from "@ucanto/interface";

@@ -43,8 +41,24 @@ /**

/**
* @param {API.InvalidCapability | API.InvalidProof | API.InvalidClaim} cause
* @param {{
* capability: API.CapabilityParser
* delegationErrors: API.DelegationError[]
* unknownCapabilities: API.Capability[]
* invalidProofs: API.InvalidProof[]
* failedProofs: API.InvalidClaim[]
* }} cause
*/
constructor(cause: API.InvalidCapability | API.InvalidProof | API.InvalidClaim);
constructor({ capability, delegationErrors, unknownCapabilities, invalidProofs, failedProofs, }: {
capability: API.CapabilityParser;
delegationErrors: API.DelegationError[];
unknownCapabilities: API.Capability[];
invalidProofs: API.InvalidProof[];
failedProofs: API.InvalidClaim[];
});
/** @type {"Unauthorized"} */
name: "Unauthorized";
cause: API.InvalidCapability | API.InvalidProof | API.InvalidClaim;
capability: API.CapabilityParser<API.Match<unknown, API.UnknownMatch>>;
delegationErrors: API.DelegationError[];
unknownCapabilities: API.Capability<API.Ability, `${string}:${string}`, any>[];
invalidProofs: API.InvalidProof[];
failedProofs: API.InvalidClaim[];
toJSON(): {

@@ -54,3 +68,2 @@ error: true;

message: string;
cause: API.InvalidCapability | API.InvalidProof | API.InvalidClaim;
stack: string | undefined;

@@ -67,3 +80,3 @@ };

* delegationErrors: API.DelegationError[]
* unknownCapaibilities: API.Capability[]
* unknownCapabilities: API.Capability[]
* invalidProofs: ProofError[]

@@ -76,3 +89,3 @@ * failedProofs: API.InvalidClaim[]

delegationErrors: API.DelegationError[];
unknownCapaibilities: API.Capability[];
unknownCapabilities: API.Capability[];
invalidProofs: ProofError[];

@@ -84,3 +97,3 @@ failedProofs: API.InvalidClaim[];

delegationErrors: API.DelegationError[];
unknownCapaibilities: API.Capability[];
unknownCapabilities: API.Capability[];
invalidProofs: ProofError[];

@@ -92,6 +105,6 @@ failedProofs: API.InvalidClaim[];

get issuer(): API.Principal<`did:${string}:${string}`>;
get capability(): any;
get delegation(): API.Delegation<API.Capabilities>;
}
import { InvalidAudience } from "./error.js";
import { capability } from "./capability.js";
import { DelegationError } from "./error.js";

@@ -109,3 +122,3 @@ declare class ProofError extends Failure {

}
export { Failure, UnavailableProof, MalformedCapability, InvalidAudience };
export { Failure, UnavailableProof, MalformedCapability, DIDKeyResolutionError as DIDResolutionError, Schema, InvalidAudience };
//# sourceMappingURL=lib.d.ts.map
{
"name": "@ucanto/validator",
"description": "UCAN RPC validators",
"version": "4.0.2",
"version": "4.0.3",
"keywords": [

@@ -24,4 +24,4 @@ "UCAN",

"@ipld/dag-cbor": "^8.0.0",
"@ucanto/core": "^4.0.2",
"@ucanto/interface": "^4.0.2",
"@ucanto/core": "^4.0.3",
"@ucanto/interface": "^4.0.3",
"multiformats": "^10.0.2"

@@ -33,4 +33,4 @@ },

"@types/mocha": "^9.1.0",
"@ucanto/client": "^4.0.2",
"@ucanto/principal": "^4.0.2",
"@ucanto/client": "^4.0.3",
"@ucanto/principal": "^4.0.3",
"c8": "^7.11.0",

@@ -37,0 +37,0 @@ "chai": "^4.3.6",

import * as API from '@ucanto/interface'
import { the } from './util.js'
import { isLink } from 'multiformats/link'

@@ -100,3 +101,3 @@ /**

describe() {
return [`Signature is invalid`].join('\n')
return [`Proof ${this.delegation.cid} signature is invalid`].join('\n')
}

@@ -121,5 +122,5 @@ }

return [
`Linked proof '${this.link}' is not included nor could be resolved`,
`Linked proof '${this.link}' is not included and could not be resolved`,
...(this.cause
? [li(`Provided resolve failed: ${this.cause.message}`)]
? [li(`Proof resolution failed with: ${this.cause.message}`)]
: []),

@@ -130,2 +131,21 @@ ].join('\n')

export class DIDKeyResolutionError extends Failure {
/**
* @param {API.UCAN.DID} did
* @param {API.Unauthorized} [cause]
*/
constructor(did, cause) {
super()
this.name = the('DIDKeyResolutionError')
this.did = did
this.cause = cause
}
describe() {
return [
`Unable to resolve '${this.did}' key`,
...(this.cause ? [li(`Resolution failed: ${this.cause.message}`)] : []),
].join('\n')
}
}
/**

@@ -146,3 +166,3 @@ * @implements {API.InvalidAudience}

describe() {
return `Delegates to '${this.delegation.audience.did()}' instead of '${this.audience.did()}'`
return `Delegation audience is '${this.delegation.audience.did()}' instead of '${this.audience.did()}'`
}

@@ -195,2 +215,3 @@ toJSON() {

}
/* c8 ignore next 3 */
describe() {

@@ -211,3 +232,5 @@ return `Encountered unknown capability: ${format(this.capability)}`

describe() {
return `Expired on ${new Date(this.delegation.expiration * 1000)}`
return `Proof ${this.delegation.cid} has expired on ${new Date(
this.delegation.expiration * 1000
)}`
}

@@ -239,3 +262,5 @@ get expiredAt() {

describe() {
return `Not valid before ${new Date(this.delegation.notBefore * 1000)}`
return `Proof ${this.delegation.cid} is not valid before ${new Date(
this.delegation.notBefore * 1000
)}`
}

@@ -245,2 +270,12 @@ get validAt() {

}
toJSON() {
const { error, name, validAt, message, stack } = this
return {
error,
name,
message,
validAt,
stack,
}
}
}

@@ -256,5 +291,5 @@

capability,
(key, value) => {
(_key, value) => {
/* c8 ignore next 2 */
if (value && value.asCID === value) {
if (isLink(value)) {
return value.toString()

@@ -261,0 +296,0 @@ } else {

import * as API from '@ucanto/interface'
import { isDelegation, UCAN } from '@ucanto/core'
import { capability } from './capability.js'
import * as Schema from './schema.js'
import {

@@ -12,10 +14,17 @@ UnavailableProof,

MalformedCapability,
DIDKeyResolutionError,
li,
} from './error.js'
export { Failure, UnavailableProof, MalformedCapability }
export {
Failure,
UnavailableProof,
MalformedCapability,
DIDKeyResolutionError as DIDResolutionError,
}
export { capability } from './capability.js'
import { DID } from './schema.js'
export * from './schema.js'
export * as Schema from './schema.js'
export { Schema }

@@ -28,3 +37,10 @@ /**

/**
* @param {Required<API.ProofResolver>} config
*
* @param {UCAN.DID} did
* @returns {API.DIDKeyResolutionError}
*/
const failDIDKeyResolution = did => new DIDKeyResolutionError(did)
/**
* @param {Required<API.ClaimOptions>} config
* @param {API.Match<unknown, API.Match>} match

@@ -54,10 +70,10 @@ */

/**
* @param {API.Delegation} delegation
* @param {API.Proof[]} proofs
* @param {Required<API.ProofResolver>} config
*/
const resolveProofs = async (delegation, config) => {
const resolveProofs = async (proofs, config) => {
/** @type {API.Result<API.Delegation, API.UnavailableProof>[]} */
const proofs = []
const delegations = []
const promises = []
for (const [index, proof] of delegation.proofs.entries()) {
for (const [index, proof] of proofs.entries()) {
if (!isDelegation(proof)) {

@@ -67,5 +83,5 @@ promises.push(

try {
proofs[index] = await config.resolve(proof)
delegations[index] = await config.resolve(proof)
} catch (error) {
proofs[index] = new UnavailableProof(
delegations[index] = new UnavailableProof(
proof,

@@ -79,3 +95,3 @@ /** @type {Error} */ (error)

} else {
proofs[index] = proof
delegations[index] = proof
}

@@ -85,3 +101,3 @@ }

await Promise.all(promises)
return proofs
return delegations
}

@@ -91,3 +107,3 @@

* @param {API.Source} from
* @param {Required<API.ProofResolver>} config
* @param {Required<API.ClaimOptions>} config
* @return {Promise<{sources:API.Source[], errors:ProofError[]}>}

@@ -99,3 +115,3 @@ */

// resolve all the proofs that can be side-loaded
const proofs = await resolveProofs(delegation, config)
const proofs = await resolveProofs(delegation.proofs, config)
for (const [index, proof] of proofs.entries()) {

@@ -106,3 +122,3 @@ // if proof can not be side-loaded save a proof errors.

} else {
// If proof does not delegate to a matchig audience save an proof error.
// If proof does not delegate to a matching audience save an proof error.
if (delegation.issuer.did() !== proof.audience.did()) {

@@ -118,3 +134,3 @@ errors.push(

// If proof is not valid (expired, not active yet or has incorrect
// signature) save a correspondig proof error.
// signature) save a corresponding proof error.
const validation = await validate(proof, config)

@@ -148,2 +164,7 @@ if (validation.error) {

/**
* Finds a valid path in a proof chain of the given `invocation` by exploring
* every possible option. On success an `Authorization` object is returned that
* illustrates the valid path. If no valid path is found `Unauthorized` error
* is returned detailing all explored paths and where they proved to fail.
*
* @template {API.Ability} A

@@ -154,36 +175,87 @@ * @template {API.URI} R

* @param {API.Invocation<API.Capability<A, URI, API.InferCaveats<C>>>} invocation
* @param {API.ValidationOptions<API.ParsedCapability<A, R, API.InferCaveats<C>>>} config
* @param {API.ValidationOptions<API.ParsedCapability<A, R, API.InferCaveats<C>>>} options
* @returns {Promise<API.Result<Authorization<API.ParsedCapability<A, R, API.InferCaveats<C>>>, API.Unauthorized>>}
*/
export const access = async (
invocation,
{ canIssue = isSelfIssued, principal, resolve = unavailable, capability }
export const access = async (invocation, { capability, ...config }) =>
claim(capability, [invocation], config)
/**
* Attempts to find a valid proof chain for the claimed `capability` given set
* of `proofs`. On success an `Authorization` object with detailed proof chain
* is returned and on failure `Unauthorized` error is returned with details on
* paths explored and why they have failed.
*
* @template {API.Ability} A
* @template {API.URI} R
* @template {API.Caveats} C
* @param {API.CapabilityParser<API.Match<API.ParsedCapability<A, R, API.InferCaveats<C>>>>} capability
* @param {API.Proof[]} proofs
* @param {API.ClaimOptions} config
* @returns {Promise<API.Result<Authorization<API.ParsedCapability<A, R, API.InferCaveats<C>>>, API.Unauthorized>>}
*/
export const claim = async (
capability,
proofs,
{
authority,
principal,
resolveDIDKey = failDIDKeyResolution,
canIssue = isSelfIssued,
resolve = unavailable,
}
) => {
const config = { canIssue, resolve, principal, capability }
const config = {
canIssue,
resolve,
principal,
capability,
authority,
resolveDIDKey,
}
const claim = capability.match({
capability: invocation.capabilities[0],
delegation: invocation,
index: 0,
})
const invalidProofs = []
if (claim.error) {
return new Unauthorized(claim)
/** @type {API.Source[]} */
const sources = []
for (const proof of await resolveProofs(proofs, config)) {
const delegation = proof.error ? proof : await validate(proof, config)
if (!delegation.error) {
for (const [index, capability] of delegation.capabilities.entries()) {
sources.push({
capability,
delegation,
index,
})
}
} else {
invalidProofs.push(delegation)
}
}
const check = await validate(invocation, config)
if (check.error) {
return new Unauthorized(check)
}
// look for the matching capability
const selection = capability.select(sources)
const match = claim.prune(config)
if (match == null) {
return new Authorization(claim, [])
} else {
const result = await authorize(match, config)
if (result.error) {
return new Unauthorized(result)
const { errors: delegationErrors, unknown: unknownCapabilities } = selection
const failedProofs = []
for (const matched of selection.matches) {
const selector = matched.prune(config)
if (selector == null) {
return new Authorization(matched, [])
} else {
return new Authorization(claim, [result])
const result = await authorize(selector, config)
if (result.error) {
failedProofs.push(result)
} else {
return new Authorization(matched, [result])
}
}
}
return new Unauthorized({
capability,
delegationErrors,
unknownCapabilities,
invalidProofs,
failedProofs,
})
}

@@ -217,8 +289,7 @@

/**
* Verifies whether any of the delegated proofs grant give capabality.
* Verifies whether any of the delegated proofs grant give capability.
*
* @template {API.ParsedCapability} C
* @template {API.Match} Match
* @param {Match} match
* @param {Required<API.ValidationOptions<C>>} config
* @param {Required<API.ClaimOptions>} config
* @returns {Promise<API.Result<Authorization<API.ParsedCapability>, API.InvalidClaim>>}

@@ -232,3 +303,3 @@ */

const selection = match.select(sources)
const { errors: delegationErrors, unknown: unknownCapaibilities } = selection
const { errors: delegationErrors, unknown: unknownCapabilities } = selection

@@ -240,3 +311,3 @@ const failedProofs = []

// @ts-expect-error - it may not be a parsed capability but rather a
// group of capabilites but we can deal with that in the future.
// group of capabilities but we can deal with that in the future.
return new Authorization(matched, [])

@@ -249,3 +320,3 @@ } else {

// @ts-expect-error - it may not be a parsed capability but rather a
// group of capabilites but we can deal with that in the future.
// group of capabilities but we can deal with that in the future.
return new Authorization(matched, [result])

@@ -259,3 +330,3 @@ }

delegationErrors,
unknownCapaibilities,
unknownCapabilities,
invalidProofs,

@@ -281,3 +352,3 @@ failedProofs,

return [
`Can not derive from prf:${this.index} - ${this.proof} because:`,
`Capability can not be derived from prf:${this.index} - ${this.proof} because:`,
li(this.cause.message),

@@ -296,3 +367,3 @@ ].join(`\n`)

* delegationErrors: API.DelegationError[]
* unknownCapaibilities: API.Capability[]
* unknownCapabilities: API.Capability[]
* invalidProofs: ProofError[]

@@ -311,5 +382,2 @@ * failedProofs: API.InvalidClaim[]

}
get capability() {
return this.info.match.value
}
get delegation() {

@@ -325,3 +393,3 @@ return this.info.match.source[0].delegation

const unknown = this.info.unknownCapaibilities.map(c =>
const unknown = this.info.unknownCapabilities.map(c =>
li(JSON.stringify(c))

@@ -331,3 +399,3 @@ )

return [
`Claimed capability ${this.info.match} is invalid`,
`Capability ${this.info.match} is not authorized because:`,
li(`Capability can not be (self) issued by '${this.issuer.did()}'`),

@@ -345,18 +413,52 @@ ...(errors.length > 0 ? errors : [li(`Delegated capability not found`)]),

*/
class Unauthorized extends Failure {
/**
* @param {API.InvalidCapability | API.InvalidProof | API.InvalidClaim} cause
* @param {{
* capability: API.CapabilityParser
* delegationErrors: API.DelegationError[]
* unknownCapabilities: API.Capability[]
* invalidProofs: API.InvalidProof[]
* failedProofs: API.InvalidClaim[]
* }} cause
*/
constructor(cause) {
constructor({
capability,
delegationErrors,
unknownCapabilities,
invalidProofs,
failedProofs,
}) {
super()
/** @type {"Unauthorized"} */
this.name = 'Unauthorized'
this.cause = cause
this.capability = capability
this.delegationErrors = delegationErrors
this.unknownCapabilities = unknownCapabilities
this.invalidProofs = invalidProofs
this.failedProofs = failedProofs
}
get message() {
return this.cause.message
describe() {
const errors = [
...this.failedProofs.map(error => li(error.message)),
...this.delegationErrors.map(error => li(error.message)),
...this.invalidProofs.map(error => li(error.message)),
]
const unknown = this.unknownCapabilities.map(c => li(JSON.stringify(c)))
return [
`Claim ${this.capability} is not authorized`,
...(errors.length > 0
? errors
: [li(`No matching delegated capability found`)]),
...(unknown.length > 0
? [li(`Encountered unknown capabilities\n${unknown.join('\n')}`)]
: []),
].join('\n')
}
toJSON() {
const { error, name, message, cause, stack } = this
return { error, name, message, cause, stack }
const { error, name, message, stack } = this
return { error, name, message, stack }
}

@@ -368,4 +470,4 @@ }

* @param {T} delegation
* @param {API.PrincipalOptions} config
* @returns {Promise<API.Result<T, API.InvalidProof>>}
* @param {Required<API.ClaimOptions>} config
* @returns {Promise<API.Result<T, API.InvalidProof|API.DIDKeyResolutionError>>}
*/

@@ -391,12 +493,79 @@ const validate = async (delegation, config) => {

* @param {T} delegation
* @param {API.PrincipalOptions} config
* @returns {Promise<API.Result<T, API.InvalidSignature>>}
* @param {Required<API.ClaimOptions>} config
* @returns {Promise<API.Result<T, API.InvalidSignature|API.DIDKeyResolutionError>>}
*/
const verifySignature = async (delegation, { principal }) => {
const issuer = principal.parse(delegation.issuer.did())
const verifySignature = async (delegation, config) => {
const did = delegation.issuer.did()
const issuer = await resolveVerifier(did, delegation, config)
if (issuer.error) {
return issuer
}
const valid = await UCAN.verifySignature(delegation.data, issuer)
return valid ? delegation : new InvalidSignature(delegation)
}
/**
* @param {API.DID} did
* @param {API.Delegation} delegation
* @param {Required<API.ClaimOptions>} config
* @returns {Promise<API.Result<API.Verifier, API.DIDKeyResolutionError>>}
*/
const resolveVerifier = async (did, delegation, config) => {
if (did === config.authority.did()) {
return config.authority
} else if (did.startsWith('did:key:')) {
return config.principal.parse(did)
} else {
// First we attempt to resolve key from the embedded proofs
const local = await resolveDIDFromProofs(did, delegation, config)
const result = !local?.error
? local
: // If failed to resolve because there is an invalid proof propagate error
(local?.cause?.failedProofs?.length || 0) > 0
? local
: // otherwise either use resolved key or if not found attempt to resolve
// did externally
await config.resolveDIDKey(did)
return result.error ? result : config.principal.parse(result).withDID(did)
}
}
/**
* @param {API.DID} did
* @param {API.Delegation} delegation
* @param {Required<API.ClaimOptions>} config
* @returns {Promise<API.Result<API.DIDKey, API.DIDKeyResolutionError>>}
*/
const resolveDIDFromProofs = async (did, delegation, config) => {
const update = Top.derive({
to: capability({
with: Schema.literal(config.authority.did()),
can: './update',
nb: { key: DID.match({ method: 'key' }) },
}),
derives: equalWith,
})
const result = await claim(update, delegation.proofs, config)
return !result.error
? result.match.value.nb.key
: new DIDKeyResolutionError(did, result)
}
const Top = capability({
can: '*',
with: DID,
})
/**
* @param {API.Capability} to
* @param {API.Capability} from
*/
const equalWith = (to, from) =>
to.with === from.with ||
new Failure(`Claimed ${to.with} can not be derived from ${from.with}`)
export { InvalidAudience }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc