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

samlify

Package Overview
Dependencies
Maintainers
1
Versions
63
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

samlify - npm Package Compare versions

Comparing version 2.4.0-rc4 to 2.4.0-rc5

.nyc_output/461b483922b44770605f300027cd82e3.json

1

build/src/binding-post.js

@@ -55,3 +55,2 @@ "use strict";

var lodash_1 = require("lodash");
var xmlTag = urn_1.tags.xmlTag;
var binding = urn_1.wording.binding;

@@ -58,0 +57,0 @@ /**

@@ -106,27 +106,2 @@ "use strict";

};
/**
* @desc Verify time stamp
* @param {date} notBefore
* @param {date} notOnOrAfter
* @return {boolean}
*/
Entity.prototype.verifyTime = function (notBefore, notOnOrAfter) {
var now = new Date();
if (lodash_1.isUndefined(notBefore) && lodash_1.isUndefined(notOnOrAfter)) {
return true; // throw exception todo
}
if (!lodash_1.isUndefined(notBefore) && lodash_1.isUndefined(notOnOrAfter)) {
var notBeforeLocal = new Date(notBefore.toUTCString());
return +notBeforeLocal <= +now;
}
if (lodash_1.isUndefined(notBefore) && !lodash_1.isUndefined(notOnOrAfter)) {
var notOnOrAfterLocal = new Date(notOnOrAfter.toUTCString());
return now < notOnOrAfterLocal;
}
else {
var notBeforeLocal = new Date(notBefore.toUTCString());
var notOnOrAfterLocal = new Date(notOnOrAfter.toUTCString());
return +notBeforeLocal <= +now && now < notOnOrAfterLocal;
}
};
/** @desc Generates the logout request for developers to design their own method

@@ -133,0 +108,0 @@ * @param {ServiceProvider} sp object of service provider

@@ -39,2 +39,3 @@ "use strict";

var utility_1 = require("./utility");
var validator_1 = require("./validator");
var libsaml_1 = require("./libsaml");

@@ -123,3 +124,3 @@ var extractor_1 = require("./extractor");

return __awaiter(this, void 0, void 0, function () {
var request, from, self, parserType, _a, checkSignature, body, direction, encodedRequest, samlContent, verificationOptions, decryptRequired, extractorFields, _b, verified, verifiedAssertionNode, result, _c, verified, verifiedAssertionNode, parseResult;
var request, from, self, parserType, _a, checkSignature, body, direction, encodedRequest, samlContent, verificationOptions, decryptRequired, extractorFields, _b, verified, verifiedAssertionNode, result, _c, verified, verifiedAssertionNode, parseResult, targetEntityMetadata, issuer, extractedProperties;
return __generator(this, function (_d) {

@@ -139,2 +140,8 @@ switch (_d.label) {

extractorFields = [];
if (!(parserType === 'SAMLResponse')) return [3 /*break*/, 2];
return [4 /*yield*/, libsaml_1.default.isValidXml(samlContent)];
case 1:
_d.sent();
_d.label = 2;
case 2:
if (parserType !== 'SAMLResponse') {

@@ -154,14 +161,8 @@ extractorFields = getDefaultExtractorFields(parserType, null);

}
if (!(parserType === 'SAMLResponse' && decryptRequired)) return [3 /*break*/, 2];
if (!(parserType === 'SAMLResponse' && decryptRequired)) return [3 /*break*/, 4];
return [4 /*yield*/, libsaml_1.default.decryptAssertion(self, samlContent)];
case 1:
case 3:
result = _d.sent();
samlContent = result[0];
extractorFields = getDefaultExtractorFields(parserType, result[1]);
_d.label = 2;
case 2:
if (!(parserType === 'SAMLResponse')) return [3 /*break*/, 4];
return [4 /*yield*/, libsaml_1.default.isValidXml(samlContent)];
case 3:
_d.sent();
_d.label = 4;

@@ -184,5 +185,23 @@ case 4:

};
// TODO: basic validator (issuer, timer)
// const targetEntityMetadata = from.entityMeta;
// const issuer = targetEntityMetadata.getEntityID();
targetEntityMetadata = from.entityMeta;
issuer = targetEntityMetadata.getEntityID();
extractedProperties = parseResult.extract;
// unmatched issuer
if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
&& extractedProperties
&& extractedProperties.issuer !== issuer) {
return [2 /*return*/, Promise.reject('ERR_UNMATCH_ISSUER')];
}
// invalid session time
if (parserType === 'SAMLResponse'
&& !validator_1.verifyTime(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter)) {
return [2 /*return*/, Promise.reject('ERR_EXPIRED_SESSION')];
}
// invalid time
// 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
if (parserType === 'SAMLResponse'
&& extractedProperties.conditions
&& !validator_1.verifyTime(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter)) {
return [2 /*return*/, Promise.reject('ERR_SUBJECT_UNCONFIRMED')];
}
return [2 /*return*/, Promise.resolve(parseResult)];

@@ -189,0 +208,0 @@ }

@@ -54,3 +54,4 @@ "use strict";

var fs = require("fs");
var Validator = require("xsd-schema-validator");
var Validator = require("@passify/xsd-schema-validator");
var extractor_1 = require("./extractor");
var signatureAlgorithms = urn_1.algorithms.signature;

@@ -282,17 +283,2 @@ var digestAlgorithms = urn_1.algorithms.digest;

}
// response must be signed, either entire document or assertion
// default we will take the assertion section under root
if (messageSignatureNode.length === 1) {
var node = xpath_1.select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc);
if (node.length === 1) {
assertionNode = node[0].toString();
}
// remove message signature
doc.removeChild(messageSignatureNode[0]);
}
if (assertionSignatureNode.length === 1) {
assertionNode = assertionSignatureNode[0].parentNode.toString();
// remove assertion signature
doc.removeChild(assertionSignatureNode[0]);
}
// guarantee to have a signature in saml response

@@ -304,3 +290,4 @@ if (selection.length === 0) {

var verified = true;
selection.forEach(function (s) {
// need to refactor later on
selection.forEach(function (signatureNode) {
var selectedCert = '';

@@ -321,3 +308,3 @@ sig.signatureAlgorithm = opts.signatureAlgorithm;

metadataCert = metadataCert.map(utility_1.default.normalizeCerString);
var x509Certificate = xpath_1.select(".//*[local-name(.)='X509Certificate']", s)[0].firstChild.data;
var x509Certificate = xpath_1.select(".//*[local-name(.)='X509Certificate']", signatureNode)[0].firstChild.data;
x509Certificate = utility_1.default.normalizeCerString(x509Certificate);

@@ -335,5 +322,49 @@ if (lodash_1.includes(metadataCert, x509Certificate)) {

}
sig.loadSignature(s);
sig.loadSignature(signatureNode);
doc.removeChild(signatureNode);
verified = verified && sig.checkSignature(doc.toString());
// immediately throw error when any one of the signature is failed to get verified
if (!verified) {
throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
}
});
// response must be signed, either entire document or assertion
// default we will take the assertion section under root
if (messageSignatureNode.length === 1) {
var node = xpath_1.select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc);
if (node.length === 1) {
assertionNode = node[0].toString();
}
}
if (assertionSignatureNode.length === 1) {
var verifiedAssertionInfo = extractor_1.extract(assertionSignatureNode[0].toString(), [{
key: 'refURI',
localPath: ['Signature', 'SignedInfo', 'Reference'],
attributes: ['URI']
}]);
// get the assertion supposed to be the one should be verified
var desiredAssertionInfo = extractor_1.extract(doc.toString(), [{
key: 'id',
localPath: ['~Response', 'Assertion'],
attributes: ['ID']
}]);
// 5.4.2 References
// SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
// the assertion or protocol message being signed. The assertion’s or protocol message's root element may
// or may not be the root element of the actual XML document containing the signed assertion or protocol
// message (e.g., it might be contained within a SOAP envelope).
// Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
// attribute value of the root element of the assertion or protocol message being signed. For example, if the
// ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
if (verifiedAssertionInfo.refURI !== "#" + desiredAssertionInfo.id) {
throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
}
var verifiedDoc = extractor_1.extract(doc.toString(), [{
key: 'assertion',
localPath: ['~Response', 'Assertion'],
attributes: [],
context: true
}]);
assertionNode = verifiedDoc.assertion.toString();
}
return [verified, assertionNode];

@@ -340,0 +371,0 @@ },

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* @file utility.ts
* @author tngan
* @desc Library for some common functions (e.g. de/inflation, en/decoding)
*/
var node_forge_1 = require("node-forge");

@@ -4,0 +9,0 @@ var deflate_js_1 = require("deflate-js");

{
"name": "samlify",
"version": "2.4.0-rc4",
"version": "2.4.0-rc5",
"description": "High-level API for Single Sign On (SAML 2.0)",

@@ -36,2 +36,3 @@ "main": "build/index.js",

"@passify/xml-encryption": "^0.11.1",
"@passify/xsd-schema-validator": "^0.7.1",
"@types/camelcase": "^0.0.30",

@@ -53,4 +54,3 @@ "@types/lodash": "^4.14.112",

"xmldom": "^0.1.19",
"xpath": "0.0.24",
"xsd-schema-validator": "^0.6.0"
"xpath": "0.0.24"
},

@@ -63,2 +63,2 @@ "devDependencies": {

}
}
}

@@ -7,3 +7,3 @@ /**

import { wording, tags, namespace, StatusCode } from './urn';
import { wording, namespace, StatusCode } from './urn';
import { BindingContext } from './entity';

@@ -14,3 +14,2 @@ import libsaml from './libsaml';

const xmlTag = tags.xmlTag;
const binding = wording.binding;

@@ -17,0 +16,0 @@

@@ -8,3 +8,3 @@ /**

import libsaml from './libsaml';
import Entity, { BindingContext } from './entity';
import { BindingContext } from './entity';
import { IdentityProvider as Idp } from './entity-idp';

@@ -141,3 +141,3 @@ import { ServiceProvider as Sp } from './entity-sp';

SessionIndex: user.sessionIndex,
}
};
if (initSetting.logoutRequestTemplate) {

@@ -144,0 +144,0 @@ const info = customTagReplacement(initSetting.logoutRequestTemplate, requiredTags);

@@ -6,3 +6,3 @@ /**

*/
import Entity, { ESamlHttpRequest, ParseResult } from './entity';
import Entity, { ESamlHttpRequest } from './entity';
import {

@@ -9,0 +9,0 @@ ServiceProviderConstructor as ServiceProvider,

@@ -133,27 +133,2 @@ /**

}
/**
* @desc Verify time stamp
* @param {date} notBefore
* @param {date} notOnOrAfter
* @return {boolean}
*/
verifyTime(notBefore?: Date, notOnOrAfter?: Date): boolean {
const now = new Date();
if (isUndefined(notBefore) && isUndefined(notOnOrAfter)) {
return true; // throw exception todo
}
if (!isUndefined(notBefore) && isUndefined(notOnOrAfter)) {
const notBeforeLocal = new Date(notBefore.toUTCString());
return +notBeforeLocal <= +now;
}
if (isUndefined(notBefore) && !isUndefined(notOnOrAfter)) {
const notOnOrAfterLocal = new Date(notOnOrAfter.toUTCString());
return now < notOnOrAfterLocal;
} else {
const notBeforeLocal = new Date(notBefore.toUTCString());
const notOnOrAfterLocal = new Date(notOnOrAfter.toUTCString());
return +notBeforeLocal <= +now && now < notOnOrAfterLocal;
}
}
/** @desc Generates the logout request for developers to design their own method

@@ -160,0 +135,0 @@ * @param {ServiceProvider} sp object of service provider

import { inflateString, base64Decode } from './utility';
import { verifyTime } from './validator';
import libsaml from './libsaml';
import { DOMParser as dom } from 'xmldom';
import {

@@ -54,3 +54,3 @@ extract,

const { SigAlg: sigAlg, Signature: signature } = query;
const targetEntityMetadata = from.entityMeta;

@@ -95,3 +95,3 @@

const decodeSigAlg = decodeURIComponent(sigAlg);
const verified = libsaml.verifyMessageSignature(targetEntityMetadata, octetString, base64Signature, sigAlg);

@@ -113,3 +113,3 @@

const {
const {
request,

@@ -138,2 +138,7 @@ from,

// validate the xml first
if (parserType === 'SAMLResponse') {
await libsaml.isValidXml(samlContent);
}
if (parserType !== 'SAMLResponse') {

@@ -150,4 +155,4 @@ extractorFields = getDefaultExtractorFields(parserType, null);

if (!verified) {
return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
}
return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
}
if (!decryptRequired) {

@@ -164,6 +169,2 @@ extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);

if (parserType === 'SAMLResponse') {
await libsaml.isValidXml(samlContent);
}
// verify the signatures (the repsonse is signed then encrypted, then decrypt first then verify)

@@ -178,3 +179,3 @@ if (

} else {
return Promise.reject('ERR_FAIL_TO_VERIFY_STE_SIGNATURE');
return Promise.reject('ERR_FAIL_TO_VERIFY_STE_SIGNATURE');
}

@@ -188,6 +189,40 @@ }

// TODO: basic validator (issuer, timer)
// const targetEntityMetadata = from.entityMeta;
// const issuer = targetEntityMetadata.getEntityID();
// validation part
const targetEntityMetadata = from.entityMeta;
const issuer = targetEntityMetadata.getEntityID();
const extractedProperties = parseResult.extract;
// unmatched issuer
if (
(parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
&& extractedProperties
&& extractedProperties.issuer !== issuer
) {
return Promise.reject('ERR_UNMATCH_ISSUER');
}
// invalid session time
if (
parserType === 'SAMLResponse'
&& !verifyTime(
undefined,
extractedProperties.sessionIndex.sessionNotOnOrAfter
)
) {
return Promise.reject('ERR_EXPIRED_SESSION');
}
// invalid time
// 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
if (
parserType === 'SAMLResponse'
&& extractedProperties.conditions
&& !verifyTime(
extractedProperties.conditions.notBefore,
extractedProperties.conditions.notOnOrAfter
)
) {
return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
}
return Promise.resolve(parseResult);

@@ -194,0 +229,0 @@ }

@@ -19,3 +19,4 @@ /**

import * as fs from 'fs';
import * as Validator from 'xsd-schema-validator';
import * as Validator from '@passify/xsd-schema-validator';
import { extract } from './extractor';

@@ -347,19 +348,3 @@ const signatureAlgorithms = algorithms.signature;

}
// response must be signed, either entire document or assertion
// default we will take the assertion section under root
if (messageSignatureNode.length === 1) {
const node = select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc);
if (node.length === 1) {
assertionNode = node[0].toString();
}
// remove message signature
doc.removeChild(messageSignatureNode[0]);
}
if (assertionSignatureNode.length === 1) {
assertionNode = assertionSignatureNode[0].parentNode.toString();
// remove assertion signature
doc.removeChild(assertionSignatureNode[0]);
}
// guarantee to have a signature in saml response

@@ -369,6 +354,7 @@ if (selection.length === 0) {

}
const sig = new SignedXml();
let verified = true;
selection.forEach(s => {
// need to refactor later on
selection.forEach(signatureNode => {
let selectedCert = '';

@@ -387,3 +373,3 @@ sig.signatureAlgorithm = opts.signatureAlgorithm;

metadataCert = metadataCert.map(utility.normalizeCerString);
let x509Certificate = select(".//*[local-name(.)='X509Certificate']", s)[0].firstChild.data;
let x509Certificate = select(".//*[local-name(.)='X509Certificate']", signatureNode)[0].firstChild.data;
x509Certificate = utility.normalizeCerString(x509Certificate);

@@ -400,6 +386,56 @@ if (includes(metadataCert, x509Certificate)) {

}
sig.loadSignature(s);
sig.loadSignature(signatureNode);
doc.removeChild(signatureNode);
verified = verified && sig.checkSignature(doc.toString());
// immediately throw error when any one of the signature is failed to get verified
if (!verified) {
throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
}
});
// response must be signed, either entire document or assertion
// default we will take the assertion section under root
if (messageSignatureNode.length === 1) {
const node = select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc);
if (node.length === 1) {
assertionNode = node[0].toString();
}
}
if (assertionSignatureNode.length === 1) {
const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [{
key: 'refURI',
localPath: ['Signature', 'SignedInfo', 'Reference'],
attributes: ['URI']
}]);
// get the assertion supposed to be the one should be verified
const desiredAssertionInfo = extract(doc.toString(), [{
key: 'id',
localPath: ['~Response', 'Assertion'],
attributes: ['ID']
}]);
// 5.4.2 References
// SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
// the assertion or protocol message being signed. The assertion’s or protocol message's root element may
// or may not be the root element of the actual XML document containing the signed assertion or protocol
// message (e.g., it might be contained within a SOAP envelope).
// Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
// attribute value of the root element of the assertion or protocol message being signed. For example, if the
// ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
if (verifiedAssertionInfo.refURI !== `#${desiredAssertionInfo.id}`) {
throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
}
const verifiedDoc = extract(doc.toString(), [{
key: 'assertion',
localPath: ['~Response', 'Assertion'],
attributes: [],
context: true
}]);
assertionNode = verifiedDoc.assertion.toString();
}
return [verified, assertionNode];

@@ -406,0 +442,0 @@ },

@@ -6,7 +6,5 @@ /**

*/
import * as fs from 'fs';
import { pki, util, asn1 } from 'node-forge';
import { inflate, deflate } from 'deflate-js';
import { isString } from 'lodash';
import { DOMParser } from 'xmldom';

@@ -13,0 +11,0 @@ const BASE64_STR = 'base64';

@@ -57,9 +57,2 @@ /// <reference types="node" />

verifyFields(field: string | string[], metaField: string): boolean;
/**
* @desc Verify time stamp
* @param {date} notBefore
* @param {date} notOnOrAfter
* @return {boolean}
*/
verifyTime(notBefore?: Date, notOnOrAfter?: Date): boolean;
/** @desc Generates the logout request for developers to design their own method

@@ -66,0 +59,0 @@ * @param {ServiceProvider} sp object of service provider

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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