diff --git a/README.md b/README.md index 4f822e50..9e25f6f1 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,10 @@ Calling one prints a `DeprecationWarning` naming its replacement. The three rege `util.deprecate` needs a call to intercept — so TypeScript users see the `@deprecated` tag and JavaScript users get no signal until the names go away. +`renderNs()` and `processInner()` on `C14nCanonicalization` and `ExclusiveCanonicalization` are +deprecated and will be private in 7.0; call `process()` instead. Calling either one from outside +`process()`, or overriding one in a subclass, prints a `DeprecationWarning`. + `toPem`, `pemToDer`, `pemCertificates`, `normalizePem` and `findAncestorNs` are **not** deprecated and stay exported. diff --git a/src/c14n-canonicalization.ts b/src/c14n-canonicalization.ts index 4611ea33..abbe283f 100644 --- a/src/c14n-canonicalization.ts +++ b/src/c14n-canonicalization.ts @@ -85,6 +85,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg * @param ancestorNamespaces Import ancestor namespaces if it is specified * @param namespacesInScope The namespaces bound on this node's output ancestors, nearest last. * Without it, a prefix in `prefixesInScope` counts as in scope whatever it is bound to. + * @deprecated Will be private in 7.0. Call {@link process} instead of calling or overriding it. * @api private */ renderNs( @@ -95,6 +96,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg ancestorNamespaces: NamespacePrefix[], namespacesInScope?: NamespacePrefix[], ): RenderedNamespace { + utils.warnOutsideProcess(this); let i; let attr; const res: string[] = []; @@ -199,6 +201,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg /** * @param node Node + * @deprecated Will be private in 7.0. Call {@link process} instead of calling or overriding it. */ processInner( node, @@ -208,6 +211,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg ancestorNamespaces, namespacesInScope?: NamespacePrefix[], ) { + utils.warnOutsideProcess(this); if (isDomNode.isDocumentNode(node)) { const res: string[] = []; for (let i = 0; i < node.childNodes.length; i++) { @@ -220,6 +224,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg } if (utils.isCanonicalDocumentChild(child)) { res.push( + /* eslint-disable-next-line deprecation/deprecation */ this.processInner( child, prefixesInScope, @@ -246,6 +251,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg if (isDomNode.isElementNode(node)) { let i; let pfxCopy; + /* eslint-disable-next-line deprecation/deprecation */ const ns = this.renderNs( node, prefixesInScope, @@ -259,6 +265,7 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg for (i = 0; i < node.childNodes.length; ++i) { pfxCopy = prefixesInScope.slice(0); res.push( + /* eslint-disable-next-line deprecation/deprecation */ this.processInner( node.childNodes[i], pfxCopy, @@ -355,15 +362,17 @@ export class C14nCanonicalization implements CanonicalizationOrTransformationAlg prefixesInScope.push(ancestorNamespaces[i].prefix); } - const res = this.processInner( - canonicalNode, - prefixesInScope, - defaultNs, - defaultNsForPrefix, - ancestorNamespaces, - ancestorNamespaces.slice(), + return utils.insideProcess(this, C14nCanonicalization.prototype, () => + /* eslint-disable-next-line deprecation/deprecation */ + this.processInner( + canonicalNode, + prefixesInScope, + defaultNs, + defaultNsForPrefix, + ancestorNamespaces, + ancestorNamespaces.slice(), + ), ); - return res; } getAlgorithmName() { diff --git a/src/exclusive-canonicalization.ts b/src/exclusive-canonicalization.ts index 210eb61d..7b61bbf6 100644 --- a/src/exclusive-canonicalization.ts +++ b/src/exclusive-canonicalization.ts @@ -79,6 +79,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati * parents which are a part of the output set * @param {String} defaultNs. The current default namespace * @return {String} + * @deprecated Will be private in 7.0. Call {@link process} instead of calling or overriding it. * @api private */ renderNs( @@ -88,6 +89,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati defaultNsForPrefix, inclusiveNamespacesPrefixList: string[], ) { + utils.warnOutsideProcess(this); let i; let attr; const res: string[] = []; @@ -169,6 +171,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati /** * @param node Node + * @deprecated Will be private in 7.0. Call {@link process} instead of calling or overriding it. */ processInner( node, @@ -177,6 +180,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati defaultNsForPrefix, inclusiveNamespacesPrefixList: string[], ) { + utils.warnOutsideProcess(this); if (isDomNode.isDocumentNode(node)) { const res: string[] = []; for (let i = 0; i < node.childNodes.length; i++) { @@ -189,6 +193,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati } if (utils.isCanonicalDocumentChild(child)) { res.push( + /* eslint-disable-next-line deprecation/deprecation */ this.processInner( child, prefixesInScope, @@ -214,6 +219,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati if (isDomNode.isElementNode(node)) { let i; let pfxCopy; + /* eslint-disable-next-line deprecation/deprecation */ const ns = this.renderNs( node, prefixesInScope, @@ -226,6 +232,7 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati for (i = 0; i < node.childNodes.length; ++i) { pfxCopy = prefixesInScope.slice(0); res.push( + /* eslint-disable-next-line deprecation/deprecation */ this.processInner( node.childNodes[i], pfxCopy, @@ -347,14 +354,10 @@ export class ExclusiveCanonicalization implements CanonicalizationOrTransformati }); } - const res = this.processInner( - node, - [], - defaultNs, - defaultNsForPrefix, - inclusiveNamespacesPrefixList, + return utils.insideProcess(this, ExclusiveCanonicalization.prototype, () => + /* eslint-disable-next-line deprecation/deprecation */ + this.processInner(node, [], defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList), ); - return res; } getAlgorithmName() { diff --git a/src/utils.ts b/src/utils.ts index 65f5142e..b8bfeb67 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,7 @@ import { X509Certificate, createPrivateKey } from "crypto"; import * as xpath from "xpath"; import type { NamespacePrefix, PemLabel } from "./types"; import * as isDomNode from "@xmldom/is-dom-node"; +import { deprecate } from "util"; export function isArrayHasLength(array: unknown): array is unknown[] { return Array.isArray(array) && array.length > 0; @@ -643,6 +644,42 @@ export function isPrefixInScope( return binding !== undefined && binding.namespaceURI === namespaceURI; } +const warnCanonicalizationInternals = deprecate( + () => {}, + "`renderNs()` and `processInner()` are deprecated and will be private in version 7.0. Call `process()` instead of calling or overriding them.", + "XML_CRYPTO_CANONICALIZATION_INTERNALS", +); + +const canonicalizationsInProcess = new WeakSet(); + +// process() goes on calling renderNs() and processInner(), so that an override of either still +// takes effect. That leaves an override, and a call from anywhere else, as the uses to warn about. +export function insideProcess(canonicalization: object, ownMethods: object, run: () => T): T { + const isOverridden = (method: string) => + Reflect.get(canonicalization, method) !== Reflect.get(ownMethods, method); + if (["renderNs", "processInner"].some(isOverridden)) { + warnCanonicalizationInternals(); + } + + // A subclass can call process() again from inside it, and the outer call is still running + // when the inner one returns. + const isOutermost = !canonicalizationsInProcess.has(canonicalization); + canonicalizationsInProcess.add(canonicalization); + try { + return run(); + } finally { + if (isOutermost) { + canonicalizationsInProcess.delete(canonicalization); + } + } +} + +export function warnOutsideProcess(canonicalization: object): void { + if (!canonicalizationsInProcess.has(canonicalization)) { + warnCanonicalizationInternals(); + } +} + export function validateDigestValue(digest, expectedDigest) { const buffer = Buffer.from(digest, "base64"); const expectedBuffer = Buffer.from(expectedDigest, "base64");