Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

saml

Package Overview
Dependencies
Maintainers
54
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

saml - npm Package Compare versions

Comparing version 2.0.1 to 3.0.0

.github/workflows/semgrep.yml

11

CHANGELOG.md

@@ -5,2 +5,13 @@ # Changelog

## [3.0.0](https://github.com/auth0/node-saml/compare/v2.0.1...v3.0.0) (2022-05-12)
### ⚠ BREAKING CHANGES
* handle poorly formatted PEM files (#85)
### Bug Fixes
* handle poorly formatted PEM files ([#85](https://github.com/auth0/node-saml/issues/85)) ([8830a23](https://github.com/auth0/node-saml/commit/8830a238d33e2e198acd81fb6d972583848bfe26))
### [2.0.1](https://github.com/auth0/node-saml/compare/v2.0.0...v2.0.1) (2022-02-09)

@@ -7,0 +18,0 @@

50

lib/utils.js

@@ -1,6 +0,6 @@

var fs = require('fs');
var Parser = require('@xmldom/xmldom').DOMParser;
const fs = require('fs');
const Parser = require('@xmldom/xmldom').DOMParser;
exports.pemToCert = function(pem) {
var cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(pem.toString());
const cert = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(pem.toString());
if (cert && cert.length > 0) {

@@ -32,7 +32,7 @@ return cert[1].replace(/[\n|\r\n]/g, '');

exports.uid = function(len) {
var buf = []
, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
, charlen = chars.length;
const buf = []
, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
, charlen = chars.length;
for (var i = 0; i < len; ++i) {
for (let i = 0; i < len; ++i) {
buf.push(chars[getRandomInt(0, charlen - 1)]);

@@ -45,12 +45,11 @@ }

exports.removeWhitespace = function(xml) {
var trimmed = xml
.replace(/\r\n/g, '')
.replace(/\n/g,'')
.replace(/>(\s*)</g, '><') //unindent
.trim();
return trimmed;
return xml
.replace(/\r\n/g, '')
.replace(/\n/g, '')
.replace(/>(\s*)</g, '><') //unindent
.trim();
};
/**
* Retrun a random int, used by `utils.uid()`
* Return a random int, used by `utils.uid()`
*

@@ -74,4 +73,4 @@ * @param {Number} min

exports.factoryForNode = function factoryForNode(pathToTemplate) {
var template = fs.readFileSync(pathToTemplate)
var prototypeDoc = new Parser().parseFromString(template.toString())
const template = fs.readFileSync(pathToTemplate);
const prototypeDoc = new Parser().parseFromString(template.toString());

@@ -82,1 +81,20 @@ return function () {

};
/**
* Standardizes PEM content to match the spec (best effort)
*
* @param pem {Buffer} The PEM content to standardize
* @returns {Buffer} The standardized PEM. Original will be returned unmodified if the content is not PEM.
*/
exports.fixPemFormatting = function (pem) {
let pemEntries = pem.toString().matchAll(/([-]{5}[^-\r\n]+[-]{5})([^-]*)([-]{5}[^-\r\n]+[-]{5})/g);
let fixedPem = ''
for (const pemParts of pemEntries) {
fixedPem = fixedPem.concat(`${pemParts[1]}\n${pemParts[2].replaceAll(/[\r\n]/g, '')}\n${pemParts[3]}\n`)
}
if (fixedPem.length === 0) {
return pem;
}
return Buffer.from(fixedPem)
}

@@ -1,4 +0,4 @@

var xmlenc = require('xml-encryption');
const xmlenc = require('xml-encryption');
var utils = require('../utils');
const utils = require('../utils');

@@ -9,3 +9,3 @@ exports.fromEncryptXmlOptions = function (options) {

} else {
var encryptOptions = {
const encryptOptions = {
rsa_pub: options.encryptionPublicKey,

@@ -33,6 +33,24 @@ pem: options.encryptionCert,

xmlenc.encrypt(xml, encryptOptions, function (err, encrypted) {
if (err) return callback(err);
callback(null, utils.removeWhitespace(encrypted));
if (err) {
// Attempt to fix errors and retry
xmlenc.encrypt(
xml,
{
...encryptOptions,
rsa_pub: utils.fixPemFormatting(encryptOptions.rsa_pub),
pem: utils.fixPemFormatting(encryptOptions.pem),
},
function (retryErr, retryEncrypted) {
if (retryErr) {
return callback(retryErr);
}
callback(null, utils.removeWhitespace(retryEncrypted));
}
);
} else {
callback(null, utils.removeWhitespace(encrypted));
}
});
};
};

@@ -1,8 +0,8 @@

var utils = require('../utils');
var SignedXml = require('xml-crypto').SignedXml;
const utils = require('../utils');
const SignedXml = require('xml-crypto').SignedXml;
var algorithms = {
const algorithms = {
signature: {
'rsa-sha256': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
'rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
'rsa-sha1': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
},

@@ -25,12 +25,12 @@ digest: {

var key = options.key;
var pem = options.cert;
var signatureAlgorithm = options.signatureAlgorithm || 'rsa-sha256';
var digestAlgorithm = options.digestAlgorithm || 'sha256';
var signatureNamespacePrefix = (function (prefix) {
const key = options.key;
const pem = options.cert;
const signatureAlgorithm = options.signatureAlgorithm || 'rsa-sha256';
const digestAlgorithm = options.digestAlgorithm || 'sha256';
const signatureNamespacePrefix = (function (prefix) {
// 0.10.1 added prefix, but we want to name it signatureNamespacePrefix - This is just to keep supporting prefix
return typeof prefix === 'string' ? prefix : '';
})(options.signatureNamespacePrefix || options.prefix);
var xpathToNodeBeforeSignature = options.xpathToNodeBeforeSignature;
var idAttribute = options.signatureIdAttribute;
const xpathToNodeBeforeSignature = options.xpathToNodeBeforeSignature;
const idAttribute = options.signatureIdAttribute;

@@ -43,33 +43,52 @@ /**

return function signXmlDocument(doc, callback) {
var unsigned = exports.unsigned(doc);
var cert = utils.pemToCert(pem);
function sign(key) {
const unsigned = exports.unsigned(doc);
const cert = utils.pemToCert(pem);
var sig = new SignedXml(null, { signatureAlgorithm: algorithms.signature[signatureAlgorithm], idAttribute: idAttribute });
sig.addReference("//*[local-name(.)='Assertion']",
["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#"],
algorithms.digest[digestAlgorithm]);
const sig = new SignedXml(null, {
signatureAlgorithm: algorithms.signature[signatureAlgorithm],
idAttribute: idAttribute
});
sig.addReference("//*[local-name(.)='Assertion']",
["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#"],
algorithms.digest[digestAlgorithm]);
sig.signingKey = key;
sig.signingKey = key;
sig.keyInfoProvider = {
getKeyInfo: function (key, prefix) {
prefix = prefix ? prefix + ':' : prefix;
return "<" + prefix + "X509Data><" + prefix + "X509Certificate>" + cert + "</" + prefix + "X509Certificate></" + prefix + "X509Data>";
}
};
sig.keyInfoProvider = {
getKeyInfo: function (key, prefix) {
prefix = prefix ? prefix + ':' : prefix;
return "<" + prefix + "X509Data><" + prefix + "X509Certificate>" + cert + "</" + prefix + "X509Certificate></" + prefix + "X509Data>";
}
};
sig.computeSignature(unsigned, {
location: { reference: xpathToNodeBeforeSignature, action: 'after' },
prefix: signatureNamespacePrefix
});
sig.computeSignature(unsigned, {
location: {reference: xpathToNodeBeforeSignature, action: 'after'},
prefix: signatureNamespacePrefix
});
var signed = sig.getSignedXml();
if (callback) {
setImmediate(callback, null, signed);
} else {
return signed;
return sig.getSignedXml();
}
let signed
try {
try {
signed = sign(key)
} catch (err) {
signed = sign(utils.fixPemFormatting(key))
}
if (callback) {
setImmediate(callback, null, signed);
} else {
return signed;
}
} catch (e) {
if (callback) {
setImmediate(callback, e)
}
throw e
}
};
};
/**

@@ -81,3 +100,3 @@ * @param {Document} doc

exports.unsigned = function (doc, callback) {
var xml = utils.removeWhitespace(doc.toString());
const xml = utils.removeWhitespace(doc.toString());
if (callback) {

@@ -84,0 +103,0 @@ setImmediate(callback, null, xml)

{
"name": "saml",
"version": "2.0.1",
"version": "3.0.0",
"engines": {

@@ -5,0 +5,0 @@ "node": ">=12"

@@ -49,2 +49,28 @@ var assert = require('chai').assert;

it('should not error when cert is missing newlines', function () {
// cert created with:
// openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key')
};
var signedAssertion = saml11[createAssertion]({...options, cert: Buffer.from(options.cert.toString().replaceAll(/[\r\n]/g, ''))});
assertSignature(signedAssertion, options);
});
it('should not error when key is missing newlines', function () {
// cert created with:
// openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key')
};
var signedAssertion = saml11[createAssertion]({...options, key: Buffer.from(options.key.toString().replaceAll(/[\r\n]/g, ''))});
assertSignature(signedAssertion, options);
});
it('should support specifying Issuer property', function () {

@@ -354,2 +380,40 @@ var options = {

it('should not error when encryptionPublicKey is missing newlines', function (done) {
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
encryptionPublicKey: Buffer.from(fs.readFileSync(__dirname + '/test-auth0_rsa.pub').toString().replaceAll(/[\r\n]/g, '')),
encryptionCert: fs.readFileSync(__dirname + '/test-auth0.pem')
};
saml11[createAssertion](options, function(err, encrypted) {
if (err) return done(err);
xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
if (err) return done(err);
assertSignature(decrypted, options);
done();
});
});
});
it('should not error when encryptionCert is missing newlines', function (done) {
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
encryptionPublicKey: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
encryptionCert: Buffer.from(fs.readFileSync(__dirname + '/test-auth0.pem').toString().replaceAll(/[\r\n]/g, ''))
};
saml11[createAssertion](options, function(err, encrypted) {
if (err) return done(err);
xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
if (err) return done(err);
assertSignature(decrypted, options);
done();
});
});
});
it('should support holder-of-key suject confirmationmethod', function (done) {

@@ -356,0 +420,0 @@ var options = {

@@ -80,2 +80,92 @@ var assert = require('chai').assert;

it('should not error when cert is missing newlines', function () {
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
issuer: 'urn:issuer',
lifetimeInSeconds: 600,
audiences: 'urn:myapp',
attributes: {
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'foo@bar.com',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'Foo Bar'
},
nameIdentifier: 'foo',
nameIdentifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'
};
var signedAssertion = saml[createAssertion]({...options, cert: Buffer.from(options.cert.toString().replaceAll(/[\r\n]/g, ''))});
assertSignature(signedAssertion, options);
var nameIdentifier = utils.getNameID(signedAssertion);
assert.equal('foo', nameIdentifier.textContent);
assert.equal('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', nameIdentifier.getAttribute('Format'));
var attributes = utils.getAttributes(signedAssertion);
assert.equal(2, attributes.length);
assert.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress', attributes[0].getAttribute('Name'));
assert.equal('foo@bar.com', attributes[0].textContent);
assert.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name', attributes[1].getAttribute('Name'));
assert.equal('Foo Bar', attributes[1].textContent);
assert.equal('urn:issuer', utils.getSaml2Issuer(signedAssertion).textContent);
var conditions = utils.getConditions(signedAssertion);
assert.equal(1, conditions.length);
var notBefore = conditions[0].getAttribute('NotBefore');
var notOnOrAfter = conditions[0].getAttribute('NotOnOrAfter');
should.ok(notBefore);
should.ok(notOnOrAfter);
var lifetime = Math.round((moment(notOnOrAfter).utc() - moment(notBefore).utc()) / 1000);
assert.equal(600, lifetime);
var authnContextClassRef = utils.getAuthnContextClassRef(signedAssertion);
assert.equal('urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified', authnContextClassRef.textContent);
});
it('should not error when key is missing newlines', function () {
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
issuer: 'urn:issuer',
lifetimeInSeconds: 600,
audiences: 'urn:myapp',
attributes: {
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'foo@bar.com',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'Foo Bar'
},
nameIdentifier: 'foo',
nameIdentifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'
};
var signedAssertion = saml[createAssertion]({...options, key: Buffer.from(options.key.toString().replaceAll(/[\r\n]/g, ''))});
assertSignature(signedAssertion, options);
var nameIdentifier = utils.getNameID(signedAssertion);
assert.equal('foo', nameIdentifier.textContent);
assert.equal('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', nameIdentifier.getAttribute('Format'));
var attributes = utils.getAttributes(signedAssertion);
assert.equal(2, attributes.length);
assert.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress', attributes[0].getAttribute('Name'));
assert.equal('foo@bar.com', attributes[0].textContent);
assert.equal('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name', attributes[1].getAttribute('Name'));
assert.equal('Foo Bar', attributes[1].textContent);
assert.equal('urn:issuer', utils.getSaml2Issuer(signedAssertion).textContent);
var conditions = utils.getConditions(signedAssertion);
assert.equal(1, conditions.length);
var notBefore = conditions[0].getAttribute('NotBefore');
var notOnOrAfter = conditions[0].getAttribute('NotOnOrAfter');
should.ok(notBefore);
should.ok(notOnOrAfter);
var lifetime = Math.round((moment(notOnOrAfter).utc() - moment(notBefore).utc()) / 1000);
assert.equal(600, lifetime);
var authnContextClassRef = utils.getAuthnContextClassRef(signedAssertion);
assert.equal('urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified', authnContextClassRef.textContent);
});
it('should set attributes', function () {

@@ -517,2 +607,44 @@ var options = {

it('should not error when encryptionPublicKey is missing newline', function (done) {
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
encryptionPublicKey: Buffer.from(fs.readFileSync(__dirname + '/test-auth0_rsa.pub').toString().replaceAll(/[\r\n]/g, '')),
encryptionCert: fs.readFileSync(__dirname + '/test-auth0.pem')
};
saml[createAssertion](options, function (err, encrypted) {
if (err) return done(err);
var encryptedData = utils.getEncryptedData(encrypted);
xmlenc.decrypt(encryptedData.toString(), { key: fs.readFileSync(__dirname + '/test-auth0.key') }, function (err, decrypted) {
if (err) return done(err);
assertSignature(decrypted, options);
done();
});
});
});
it('should not error when encryptionCert is missing newline', function (done) {
var options = {
cert: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
encryptionPublicKey: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
encryptionCert: Buffer.from(fs.readFileSync(__dirname + '/test-auth0.pem').toString().replaceAll(/[\r\n]/g, ''))
};
saml[createAssertion](options, function (err, encrypted) {
if (err) return done(err);
var encryptedData = utils.getEncryptedData(encrypted);
xmlenc.decrypt(encryptedData.toString(), { key: fs.readFileSync(__dirname + '/test-auth0.key') }, function (err, decrypted) {
if (err) return done(err);
assertSignature(decrypted, options);
done();
});
});
});
it('should set attributes', function (done) {

@@ -519,0 +651,0 @@ var options = {

@@ -1,11 +0,63 @@

var assert = require("assert"),
utils = require("../lib/utils");
const assert = require("assert"),
utils = require("../lib/utils");
const fs = require("fs");
const {createPublicKey} = require('crypto')
describe("saml 1.1", function() {
describe("pemToCert", function() {
it("should not throw when the cert is invalid", function() {
var cert = utils.pemToCert('abc');
assert.ok(!cert);
});
});
describe("pemToCert", function () {
it("should not throw when the cert is invalid", function () {
var cert = utils.pemToCert('abc');
assert.ok(!cert);
});
});
describe("fixPemFormatting", () => {
it("returns the original when the original is not in PEM format", () => {
let originalCert = fs.readFileSync(__dirname + '/test-auth0.der');
let standardizedCert = utils.fixPemFormatting(originalCert);
assert.strictEqual(originalCert.compare(standardizedCert), 0);
})
it("handles already correctly formatted PEM content", () => {
let originalCert = fs.readFileSync(__dirname + '/test-auth0_rsa.pub');
let standardizedCert = utils.fixPemFormatting(originalCert);
assert.notStrictEqual(originalCert, standardizedCert);
assert.deepStrictEqual(createPublicKey(originalCert), createPublicKey(standardizedCert));
})
it("handles PEM content with extra data before the cert", () => {
let originalCert = Buffer.from(`data that should be ignored\n${fs.readFileSync(__dirname + '/test-auth0_rsa.pub').toString()}`)
let standardizedCert = utils.fixPemFormatting(originalCert);
assert.notStrictEqual(originalCert, standardizedCert);
assert.deepStrictEqual(createPublicKey(originalCert), createPublicKey(standardizedCert));
})
it("handles PEM content with extra data after the cert", () => {
let originalCert = Buffer.from(`${fs.readFileSync(__dirname + '/test-auth0_rsa.pub').toString()}\ndata that should be ignored`)
let standardizedCert = utils.fixPemFormatting(originalCert);
assert.notStrictEqual(originalCert, standardizedCert);
assert.deepStrictEqual(createPublicKey(originalCert), createPublicKey(standardizedCert));
})
it("handles incorrectly formatted PEM content", () => {
let originalCert = Buffer.from(fs.readFileSync(__dirname + '/test-auth0_rsa.pub').toString().replaceAll(/[\r\n]/g, ''));
let standardizedCert = utils.fixPemFormatting(originalCert);
assert.notStrictEqual(originalCert, standardizedCert);
let correctCert = createPublicKey(fs.readFileSync(__dirname + '/test-auth0_rsa.pub'))
assert.deepStrictEqual(correctCert, createPublicKey(standardizedCert));
})
it("handles already correctly formatted PEM chains", () => {
let originalCert = fs.readFileSync(__dirname + '/test-auth0-chain.pem');
let standardizedCert = utils.fixPemFormatting(originalCert);
assert.notStrictEqual(originalCert, standardizedCert);
assert.deepStrictEqual(createPublicKey(originalCert), createPublicKey(standardizedCert));
})
it("handles incorrectly formatted PEM chains", () => {
let originalCert = Buffer.from(fs.readFileSync(__dirname + '/test-auth0-chain.pem').toString().replaceAll(/[\r\n]/g, ''));
let standardizedCert = utils.fixPemFormatting(originalCert);
assert.notStrictEqual(originalCert, standardizedCert);
let correctCert = createPublicKey(fs.readFileSync(__dirname + '/test-auth0-chain.pem'))
assert.deepStrictEqual(correctCert, createPublicKey(standardizedCert));
})
})

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