Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for directly querying a node to see if it has passed validation #389

Merged
merged 5 commits into from
Oct 7, 2023

Conversation

cjbarth
Copy link
Contributor

@cjbarth cjbarth commented Sep 18, 2023

Previously, the API for checking if a reference was valid was very complex. This adds support for allow for a single method call on the container that represents the reference to determine if it passed validation. The idea is that this can clean up some code as found at node-saml:

export const validateXmlSignatureForCert = (
  signature: Node,
  certPem: string,
  fullXml: string,
  currentNode: Element
): boolean => {
  const sig = new xmlCrypto.SignedXml();
  sig.keyInfoProvider = {
    file: "",
    getKeyInfo: () => "<X509Data></X509Data>",
    getKey: () => Buffer.from(certPem),
  };
  sig.loadSignature(signature);
  // We expect each signature to contain exactly one reference to the top level of the xml we
  //   are validating, so if we see anything else, reject.
  if (sig.references.length != 1) return false;
  const refUri = sig.references[0].uri;
  assertRequired(refUri, "signature reference uri not found");
  const refId = refUri[0] === "#" ? refUri.substring(1) : refUri;
  // If we can't find the reference at the top level, reject
  const idAttribute = currentNode.getAttribute("ID") ? "ID" : "Id";
  if (currentNode.getAttribute(idAttribute) != refId) return false;
  // If we find any extra referenced nodes, reject.  (xml-crypto only verifies one digest, so
  //   multiple candidate references is bad news)
  const totalReferencedNodes = xpath.selectElements(
    currentNode.ownerDocument,
    "//*[@" + idAttribute + "='" + refId + "']"
  );

  if (totalReferencedNodes.length > 1) {
    return false;
  }
  fullXml = normalizeNewlines(fullXml);
  return sig.checkSignature(fullXml);
};

Which code follows the recommendation in the README:

// Roll your own
var elem = xpath.select("/xpath_to_interesting_element", doc);
var uri = sig.references[0].uri; // might not be 0 - depending on the document you verify
var id = uri[0] === "#" ? uri.substring(1) : uri;
if (elem.getAttribute("ID") != id && elem.getAttribute("Id") != id && elem.getAttribute("id") != id)
  throw new Error("the interesting element was not the one verified by the signature");

// Use the built-in method
let elem = xpath.select("/xpath_to_interesting_element", doc);
try {
  const matchingReference = sig.validateElementAgainstReferences(elem, doc);
} catch {
  throw new Error("the interesting element was not the one verified by the signature");
}

Since we already know about the id internally, this shouldn't have to be re-implemented by a consumer.

@codecov
Copy link

codecov bot commented Sep 18, 2023

Codecov Report

Merging #389 (6399cc6) into master (f0237e9) will increase coverage by 0.20%.
The diff coverage is 85.71%.

@@            Coverage Diff             @@
##           master     #389      +/-   ##
==========================================
+ Coverage   74.03%   74.24%   +0.20%     
==========================================
  Files           9        9              
  Lines         882      889       +7     
  Branches      233      236       +3     
==========================================
+ Hits          653      660       +7     
+ Misses        136      135       -1     
- Partials       93       94       +1     
Files Coverage Δ
src/types.ts 90.00% <ø> (ø)
src/signed-xml.ts 76.84% <85.71%> (+0.39%) ⬆️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@cjbarth
Copy link
Contributor Author

cjbarth commented Sep 18, 2023

@LoneRifle If you like this approach, or think a feature like this has merit, I'll add some tests.

@LoneRifle
Copy link
Collaborator

My dev tooling is not within reach currently; will be a few days before I can get round to looking!

@cjbarth
Copy link
Contributor Author

cjbarth commented Sep 30, 2023

@LoneRifle , I'm eager to hear what you think. I'd like to get this landed and released so that I can use it in the pending release of node-saml. I appreciate you buying out time to have a look.

LoneRifle
LoneRifle previously approved these changes Oct 1, 2023
Copy link
Collaborator

@LoneRifle LoneRifle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for taking so long!

lgtm, but how is this going to be used? Should we update the README to explain the new feature?

@cjbarth
Copy link
Contributor Author

cjbarth commented Oct 2, 2023

Yes, I'd update the README with this new process. I'll actually write a test that shows the old way working to validate and the new way working to validate. Now that you're OK with the general direction, I'll start to move forward on the README and tests.

@cjbarth cjbarth force-pushed the validated-node branch 2 times, most recently from a473cb4 to 0ff0aaa Compare October 6, 2023 19:22
@cjbarth
Copy link
Contributor Author

cjbarth commented Oct 6, 2023

I've finished with this code and am breaking it into smaller pieces to land as functional commits and to make review easier.

@cjbarth
Copy link
Contributor Author

cjbarth commented Oct 6, 2023

@LoneRifle Hopefully this gives a clearer idea of how this would be used. Please have a look and approve this again if you agree with this methodology. I'm 100% read to land this unless you have feedback.

After this, I'll make a PR for pulling isDomNode from NPM instead of GitHub, and then we can release this as a semver-major and get on to node-saml work :)

Copy link
Collaborator

@LoneRifle LoneRifle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm

@cjbarth cjbarth merged commit 2aa2d13 into node-saml:master Oct 7, 2023
8 checks passed
@cjbarth cjbarth deleted the validated-node branch October 7, 2023 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants