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

Clean up signature tests #178

Merged
merged 1 commit into from
Sep 21, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 106 additions & 53 deletions test/test-signatures.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,76 @@ import { expect } from "chai";
const cert = fs.readFileSync(__dirname + "/static/cert.pem", "ascii");

describe("Signatures", function () {
const INVALID_SIGNATURE = "Invalid signature",
INVALID_DOCUMENT_SIGNATURE = "Invalid document signature",
INVALID_ENCRYPTED_SIGNATURE = "Invalid signature from encrypted assertion",
INVALID_TOO_MANY_TRANSFORMS = "Invalid signature, too many transforms",
createBody = (pathToXml: string) => ({
SAMLResponse: fs.readFileSync(__dirname + "/static/signatures" + pathToXml, "base64"),
}),
testOneResponseBody = async (
samlResponseBody: Record<string, string>,
shouldErrorWith: string | false | undefined,
amountOfSignatureChecks = 1,
options: Partial<SamlConfig> = {}
) => {
//== Instantiate new instance before every test
const samlObj = new SAML({
cert,
issuer: options.issuer ?? "onesaml_login",
wantAuthnResponseSigned: false,
...options,
});

//== Spy on `validateSignature` to be able to count how many times it has been called
const validateSignatureSpy = sinon.spy(xml, "validateSignature");

try {
//== Run the test in `func`
const INVALID_SIGNATURE = "Invalid signature";
const INVALID_DOCUMENT_SIGNATURE = "Invalid document signature";
const INVALID_ENCRYPTED_SIGNATURE = "Invalid signature from encrypted assertion";
const INVALID_TOO_MANY_TRANSFORMS = "Invalid signature, too many transforms";

const createBody = (pathToXml: string) => ({
SAMLResponse: fs.readFileSync(__dirname + "/static/signatures" + pathToXml, "base64"),
});

const testOneResponseBody = async (
samlResponseBody: Record<string, string>,
shouldErrorWith: string | false | undefined,
amountOfSignatureChecks = 1,
options: Partial<SamlConfig> = {}
) => {
//== Instantiate new instance before every test
const samlObj = new SAML({
cert,
issuer: options.issuer ?? "onesaml_login",
audience: false,
wantAuthnResponseSigned: false,
...options,
});

//== Spy on `validateSignature` to be able to count how many times it has been called
const validateSignatureSpy = sinon.spy(xml, "validateSignature");

try {
//== Run the test in `func`
if (shouldErrorWith === false) {
await assert.doesNotReject(samlObj.validatePostResponseAsync(samlResponseBody));
} else {
await assert.rejects(samlObj.validatePostResponseAsync(samlResponseBody), {
message: shouldErrorWith || "SAML assertion expired: clocks skewed too much",
message: shouldErrorWith,
});
//== Assert times `validateSignature` was called
expect(validateSignatureSpy.callCount).to.equal(amountOfSignatureChecks);
} finally {
validateSignatureSpy.restore();
}
},
testOneResponse = (
pathToXml: string,
shouldErrorWith: string | false,
amountOfSignaturesChecks: number | undefined,
options?: Partial<SamlConfig>
) => {
//== Create a body based on an XML and run the test
return async () =>
await testOneResponseBody(
createBody(pathToXml),
shouldErrorWith,
amountOfSignaturesChecks,
options
);
};
//== Assert times `validateSignature` was called
expect(validateSignatureSpy.callCount).to.equal(amountOfSignatureChecks);
} finally {
validateSignatureSpy.restore();
}
};

const testOneResponse = (
pathToXml: string,
shouldErrorWith: string | false,
amountOfSignaturesChecks: number | undefined,
options?: Partial<SamlConfig>
) => {
//== Create a body based on an XML and run the test
return async () =>
await testOneResponseBody(
createBody(pathToXml),
shouldErrorWith,
amountOfSignaturesChecks,
options
);
};

describe("Signatures on saml:Response - Only 1 saml:Assertion", () => {
let fakeClock: sinon.SinonFakeTimers;

beforeEach(function () {
fakeClock = sinon.useFakeTimers(Date.parse("2020-09-25T16:59:00Z"));
});

afterEach(function () {
fakeClock.restore();
});

//== VALID
it(
"R1A - both signed => valid",
Expand All @@ -69,7 +87,7 @@ describe("Signatures", function () {
it(
"R1A - root signed, root signiture required => valid",
testOneResponse("/valid/response.root-signed.assertion-unsigned.xml", false, 1, {
issuer: "onesaml_login",
wantAuthnResponseSigned: true,
})
);
it(
Expand All @@ -90,7 +108,6 @@ describe("Signatures", function () {
1,
{
wantAuthnResponseSigned: true,
issuer: "onesaml_login",
}
)
);
Expand Down Expand Up @@ -118,7 +135,6 @@ describe("Signatures", function () {
"R1A - root signed - wantAssertionsSigned=true => error",
testOneResponse("/valid/response.root-signed.assertion-unsigned.xml", INVALID_SIGNATURE, 2, {
wantAssertionsSigned: true,
issuer: "onesaml_login",
})
);
it(
Expand All @@ -130,7 +146,6 @@ describe("Signatures", function () {
{
decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"),
wantAssertionsSigned: true,
issuer: "onesaml_login",
}
)
);
Expand All @@ -142,7 +157,6 @@ describe("Signatures", function () {
2,
{
wantAssertionsSigned: true,
issuer: "onesaml_login",
}
)
);
Expand All @@ -155,7 +169,6 @@ describe("Signatures", function () {
{
decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"),
wantAssertionsSigned: true,
issuer: "onesaml_login",
}
)
);
Expand All @@ -178,6 +191,16 @@ describe("Signatures", function () {
});

describe("Signatures on saml:Response - 1 saml:Assertion + 1 saml:Advice containing 1 saml:Assertion", () => {
let fakeClock: sinon.SinonFakeTimers;

beforeEach(function () {
fakeClock = sinon.useFakeTimers(Date.parse("2020-09-25T16:59:00Z"));
});

afterEach(function () {
fakeClock.restore();
});

//== VALID
it(
"R1A1Ad - signed root+asrt+advi => valid",
Expand Down Expand Up @@ -260,6 +283,16 @@ describe("Signatures", function () {
});

describe("Signatures on saml:Response - 1 saml:Assertion + 1 saml:Advice containing 2 saml:Assertion", () => {
let fakeClock: sinon.SinonFakeTimers;

beforeEach(function () {
fakeClock = sinon.useFakeTimers(Date.parse("2020-09-25T16:59:00Z"));
});

afterEach(function () {
fakeClock.restore();
});

//== VALID
it(
"R1A2Ad - signed root+asrt+advi => valid",
Expand Down Expand Up @@ -314,6 +347,16 @@ describe("Signatures", function () {
});

describe("Signature on saml:Response with non-LF line endings", () => {
let fakeClock: sinon.SinonFakeTimers;

beforeEach(function () {
fakeClock = sinon.useFakeTimers(Date.parse("2020-09-25T16:59:00Z"));
});

afterEach(function () {
fakeClock.restore();
});

const samlResponseXml = fs
.readFileSync(
__dirname + "/static/signatures/valid/response.root-signed.assertion-signed.xml"
Expand All @@ -333,6 +376,16 @@ describe("Signatures", function () {
});

describe("Signature on saml:Response with XML-encoded carriage returns", () => {
let fakeClock: sinon.SinonFakeTimers;

beforeEach(function () {
fakeClock = sinon.useFakeTimers(Date.parse("2020-09-25T16:59:00Z"));
});

afterEach(function () {
fakeClock.restore();
});

it(
"Attribute with with &#13;",
testOneResponse("/valid/response.root-signed.assertion-unsigned-13.xml", false, 1)
Expand Down