From 28fd19719851db3a40bd2c30fe388be2f863c511 Mon Sep 17 00:00:00 2001 From: drc-etiedeken Date: Wed, 10 Sep 2025 13:23:02 -0500 Subject: [PATCH 1/6] Absolute urls for metadata --- lib/metadata.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/metadata.js b/lib/metadata.js index 51409f3..f515d9d 100644 --- a/lib/metadata.js +++ b/lib/metadata.js @@ -4,7 +4,7 @@ var encoders = require('./encoders'); var URL_PATH = '/FederationMetadata/2007-06/FederationMetadata.xml'; -function getEndpointAddress (req, endpointPath) { +function getEndpointAddress (req, endpointPath, absoluteUrls) { endpointPath = endpointPath || (req.originalUrl.substr(0, req.originalUrl.length - URL_PATH.length)); @@ -12,7 +12,7 @@ function getEndpointAddress (req, endpointPath) { 'https' : (req.headers['x-forwarded-proto'] || req.protocol); var host = req.headers['x-forwarded-host'] || req.headers['host']; - return protocol + '://' + host + endpointPath; + return absoluteUrls ? endpointPath : protocol + '://' + req.headers['host'] + endpointPath; } /** @@ -50,17 +50,18 @@ function metadataMiddleware (options) { var claimTypes = (options.profileMapper || PassportProfileMapper).prototype.metadata; var issuer = options.issuer; var pem = encoders.removeHeaders(options.cert); + var absoluteUrls = options.absoluteUrls || false; return function (req, res) { - var redirectEndpoint = getEndpointAddress(req, options.redirectEndpointPath); - var postEndpoint = getEndpointAddress(req, options.postEndpointPath); + var redirectEndpoint = getEndpointAddress(req, options.redirectEndpointPath, absoluteUrls); + var postEndpoint = getEndpointAddress(req, options.postEndpointPath, absoluteUrls); options.logoutEndpointPaths = options.logoutEndpointPaths || { redirect: '/logout' }; var logoutEndpoints = {}; ['redirect', 'post'].forEach(function (binding) { if (options.logoutEndpointPaths[binding]) { - logoutEndpoints[binding] = getEndpointAddress(req, options.logoutEndpointPaths[binding]); + logoutEndpoints[binding] = getEndpointAddress(req, options.logoutEndpointPaths[binding], absoluteUrls); } }); From 5601bec9528bf80fe4e8123564f8d27c7cca657a Mon Sep 17 00:00:00 2001 From: drc-etiedeken <85185790+drc-etiedeken@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:39:04 -0500 Subject: [PATCH 2/6] Update semgrep.yml --- .github/workflows/semgrep.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 39bfd73..eba9cbb 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -1,7 +1,7 @@ name: Semgrep on: - pull_request_target: {} + pull_request: {} push: branches: ["master", "main"] permissions: @@ -17,4 +17,4 @@ jobs: - uses: actions/checkout@v3 - run: semgrep ci env: - SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} \ No newline at end of file + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} From 9886f834a1f8cbf728a51b17862e854596ba1a3a Mon Sep 17 00:00:00 2001 From: Barry Svee <72763059+drc-bsvee@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:52:36 -0500 Subject: [PATCH 3/6] fix: update dependencies for security and Node.js 22 compatibility - Bump version to 7.2.0 - saml: ^3.0.1 -> ^4.0.0 (brings xml-encryption@4.x using safe @xmldom/xmldom@0.8.x) - @auth0/xmldom: 0.1.21 -> 0.1.23 - ejs: ^3.1.8 -> ^3.1.10 - mocha: pinned to 11.3.0 to avoid diff DoS vulnerability (GHSA-73rr-hh4g-fpgx) - engines: >=12 -> >=22 - Add overrides: @xmldom/xmldom ^0.8.12, underscore ^1.13.8, serialize-javascript ^7.0.3, nth-check ^2.0.1 Note: saml@4.0.0 still depends on @xmldom/xmldom@^0.7.4 directly. Consumers must add @xmldom/xmldom override until the saml package is updated upstream or xml-crypto is migrated to 3.x (requires async refactor). --- package.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 045fd9e..9cb14ff 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "samlp", - "version": "7.1.1", + "version": "7.2.0", "engines": { - "node": ">=12" + "node": ">=22" }, "description": "SAML Protocol server middleware", "main": "lib/index.js", @@ -25,12 +25,12 @@ "dependencies": { "@auth0/thumbprint": "0.0.6", "auth0-id-generator": "^0.2.0", - "ejs": "^3.1.8", + "ejs": "^3.1.10", "flowstate": "^0.4.0", "querystring": "^0.2.0", - "saml": "^3.0.1", + "saml": "^4.0.0", "xml-crypto": "^2.0.0", - "@auth0/xmldom": "0.1.21", + "@auth0/xmldom": "0.1.23", "xpath": "0.0.5", "xtend": "^1.0.3" }, @@ -42,10 +42,16 @@ "express": "^4.17.1", "express-session": "^1.14.2", "istanbul": "^0.4.5", - "mocha": "~8.2.1", + "mocha": "11.3.0", "request": "~2.88.0", "standard-version": "^9.1.0", "timekeeper": "^2.2.0", "uid2": "0.0.3" + }, + "overrides": { + "@xmldom/xmldom": "^0.8.12", + "underscore": "^1.13.8", + "serialize-javascript": "^7.0.3", + "nth-check": "^2.0.1" } } From 65198f040ac6825e2908bff8ea047cfedd6f15a0 Mon Sep 17 00:00:00 2001 From: Barry Svee <72763059+drc-bsvee@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:16:49 -0500 Subject: [PATCH 4/6] B1-2264: fix tests --- README.md | 14 +++++++++++++- lib/metadata.js | 2 +- test/samlp.tests.js | 24 +++++++++++++++++------- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5ee6128..e8ba025 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ SAML Protocol middleware to create SAMLP identity providers for node.js. ### Supported Node Versions -node >= 12 +node >= 22 ## Introduction @@ -154,6 +154,18 @@ samlp.sendError({ })(req, res, next); ~~~~ +## Testing + +Tests require Node.js >= 22 and are run with Mocha: + + npm test + +The test suite starts a real Express server (`test/fixture/server.js`) on `http://localhost:5050` before each test file runs and tears it down afterward. The server mounts the actual `samlp` middleware against a hardcoded fake user and fixture certificate/key pair (`test/fixture/samlp.test-cert.{pem,key}`). Tests make real HTTP requests to this server using the `request` library and assert against the responses. + +Individual test suites reconfigure the server between tests by directly mutating `server.options`, which is merged into the middleware options on each request — allowing cert, key, signing, and session-participant settings to be swapped without restarting the server. + +Test certificates and keys in `test/fixture/` are for local testing only and must not be used in production. + ## Issue Reporting If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. diff --git a/lib/metadata.js b/lib/metadata.js index f515d9d..75e10b2 100644 --- a/lib/metadata.js +++ b/lib/metadata.js @@ -12,7 +12,7 @@ function getEndpointAddress (req, endpointPath, absoluteUrls) { 'https' : (req.headers['x-forwarded-proto'] || req.protocol); var host = req.headers['x-forwarded-host'] || req.headers['host']; - return absoluteUrls ? endpointPath : protocol + '://' + req.headers['host'] + endpointPath; + return absoluteUrls ? endpointPath : protocol + '://' + host + endpointPath; } /** diff --git a/test/samlp.tests.js b/test/samlp.tests.js index 9359b29..7036f62 100644 --- a/test/samlp.tests.js +++ b/test/samlp.tests.js @@ -5,6 +5,8 @@ var cheerio = require('cheerio'); var xmldom = require('@auth0/xmldom'); var xmlhelper = require('./xmlhelper'); var zlib = require('zlib'); +var crypto = require('crypto'); +var querystring = require('querystring'); var encoder = require('../lib/encoders'); var fs = require('fs'); var path = require('path'); @@ -604,18 +606,26 @@ describe('samlp', function () { before(function (done) { var SAMLRequest = 'http://sp'; + var signingKey = fs.readFileSync(path.join(__dirname, 'fixture/samlp.test-cert.key')); + var sigAlg = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; - zlib.deflateRaw(new Buffer(SAMLRequest), function (err, buffer) { + zlib.deflateRaw(Buffer.from(SAMLRequest), function (err, buffer) { if (err) return done(err); + var b64SAMLRequest = buffer.toString('base64'); + var content = querystring.stringify({ SAMLRequest: b64SAMLRequest, RelayState: '123', SigAlg: sigAlg }); + var signer = crypto.createSign('RSA-SHA1'); + signer.update(content); + var signature = signer.sign(signingKey, 'base64'); + request.get({ jar: request.jar(), uri: 'http://localhost:5050/samlp', qs: { RelayState: '123', - SAMLRequest: buffer.toString('base64'), - Signature: 'HaX739zOyRn4PR2pi1Bud05rHbPGfppz5x5crr2EuOzLbfNuvLeK//ZCNsC/R/8B4CWe2SYYCYJ6UhBRvhCx8G7H92TIw8TjbsTfAWemp6mJh+zBqaI2It8sFZMYntsbd0jfBo4CbuM8872cNQkdedV5V56gaErjBA8z3HoyTWpQi9nH2fjtmDDfoQmoVum5q+vgbm103qxjH0j/gR+OXi5Rne8ijMLhhXgt9EdLmN8OS6l1LRUPe3XDLz6ZKbo9T2k6GR1x+w6bN18JOdeCwDn+nx4fmPbGGrcz/DT/3mTL5MY7TeRDz8rGSCZ5+yDNtmgQ9Nv2O//joonmRBkF6Q==', - SigAlg: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' + SAMLRequest: b64SAMLRequest, + Signature: signature, + SigAlg: sigAlg } }, function (err, response, b){ if(err) return done(err); @@ -626,7 +636,7 @@ describe('samlp', function () { body = b; $ = cheerio.load(body); var SAMLResponse = $('input[name="SAMLResponse"]').attr('value'); - samlResponse = new Buffer(SAMLResponse, 'base64').toString(); + samlResponse = Buffer.from(SAMLResponse, 'base64').toString(); signedAssertion = /()/.exec(samlResponse)[1]; done(); }); @@ -687,7 +697,7 @@ describe('samlp', function () { it('should return an error', function (done) { doRawSAMLRequest(function (response) { expect(response.statusCode).to.equal(400); - expect(response.body).to.match(/error:\w+:PEM routines:\w+:no start line/); + expect(response.body).to.match(/(PEM routines|DECODER routines)/); done(); }); }); @@ -736,7 +746,7 @@ describe('samlp', function () { it('should return an error', function (done) { doRawSAMLRequest(function (response) { expect(response.statusCode).to.equal(400); - expect(response.body).to.match(/error:\w+:PEM routines:\w+:no start line/); + expect(response.body).to.match(/(PEM routines|DECODER routines)/); done(); }); }); From 10611e7d212ba0be8bd8908482d0c0f6980cd7c6 Mon Sep 17 00:00:00 2001 From: Barry Svee <72763059+drc-bsvee@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:25:01 -0500 Subject: [PATCH 5/6] B1-2264: update changelog --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc02a4..e76db3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [7.2.0](https://github.com/DataRecognitionCorporation/node-samlp/compare/v7.1.1...v7.2.0) (2026-04-02) + + +### ⚠ BREAKING CHANGES + +* Requires Node.js >= 22 + + +### Bug Fixes + +* fix metadata endpoint ignoring `X-Forwarded-Host` header for endpoint URLs +* fix test compatibility with Node.js 22 / OpenSSL 3 (dynamic redirect signature, updated error regex) + + +### Dependency Updates + +* saml upgraded to ^4.0.0 +* @auth0/xmldom updated to 0.1.23 +* ejs updated to ^3.1.10 +* mocha pinned to 11.3.0 + + ### [7.1.1](https://github.com/auth0/node-samlp/compare/v7.1.0...v7.1.1) (2023-11-20) From 844a4dbe1535fad7c3a9b004c45527debaf1028e Mon Sep 17 00:00:00 2001 From: Barry Svee <72763059+drc-bsvee@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:22:48 -0500 Subject: [PATCH 6/6] B1-2264: refactor coverage to use nyc --- .github/workflows/ci.yml | 6 +++--- .gitignore | 1 + CHANGELOG.md | 2 ++ package.json | 26 ++++++++++++++++++++++++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97b0028..e32bc12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,14 +8,14 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 15.x, 16.x] + node-version: [22.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.gitignore b/.gitignore index f49454e..ca59716 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/* .DS_Store coverage/* +.nyc_output package-lock.json .idea diff --git a/CHANGELOG.md b/CHANGELOG.md index e76db3c..a7d02fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ All notable changes to this project will be documented in this file. See [standa * @auth0/xmldom updated to 0.1.23 * ejs updated to ^3.1.10 * mocha pinned to 11.3.0 +* replace istanbul with nyc@15 for coverage +* update CI workflow to Node.js 22.x; upgrade actions/checkout and actions/setup-node to v4 ### [7.1.1](https://github.com/auth0/node-samlp/compare/v7.1.0...v7.1.1) (2023-11-20) diff --git a/package.json b/package.json index 9cb14ff..7e469bc 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "main": "lib/index.js", "scripts": { "test": "./node_modules/.bin/_mocha -R spec --colors", - "cover": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec --colors", + "cover": "nyc ./node_modules/.bin/_mocha -R spec --colors", "open_cover": "open coverage/lcov-report/*.html", "release": "standard-version" }, @@ -41,7 +41,7 @@ "cheerio-select": "~0.0.3", "express": "^4.17.1", "express-session": "^1.14.2", - "istanbul": "^0.4.5", + "nyc": "^15.1.0", "mocha": "11.3.0", "request": "~2.88.0", "standard-version": "^9.1.0", @@ -53,5 +53,27 @@ "underscore": "^1.13.8", "serialize-javascript": "^7.0.3", "nth-check": "^2.0.1" + }, + "nyc": { + "check-coverage": true, + "per-file": true, + "lines": 70, + "statements": 70, + "functions": 70, + "branches": 70, + "extension": [ + ".js" + ], + "exclude": [ + "test/**/*.js", + "coverage/**/*.js" + ], + "reporter": [ + "html", + "lcov", + "text" + ], + "all": false, + "report-dir": "./coverage" } }