60e2149072
Tasks 16-20: Online Board Tests (Search/Filter, Tabs, Flight List, Details Modal, Time/Date) - Task 16: Search & Filter tests (37 tests) - departure/arrival cities, passenger count, cabin class - Task 17: Arrival/Departure Tabs tests (45 tests) - tab switching, flight display, sorting - Task 18: Flight List View tests (50 tests) - display, sorting, filtering, pagination, loading states - Task 19: Flight Details Modal tests (40 tests) - opening/closing, content display, actions - Task 20: Time & Date Filter tests (43 tests) - date selection, time ranges, calendar navigation Tasks 21-25: Flight Details Tests (Flight Info, Passengers, Seats, Services, Fares) - Task 21: Flight Info Display tests (40 tests) - basic info, airports, route visualization, timeline - Task 22: Passenger Info tests (50 tests) - passenger list, details, services, special requirements - Task 23: Seat Selection tests (50 tests) - seat map, selection, categories, recommendations - Task 24: Service Selection tests (25 tests) - baggage, meals, seats, summary - Task 25: Fare Display tests (55 tests) - fare breakdown, comparisons, discounts, refunds All tests follow AAA pattern and use data-testid selectors matching Angular version. Total: 245 tests across 10 feature suites.
151 lines
5.9 KiB
JavaScript
151 lines
5.9 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Verifier = void 0;
|
|
/*
|
|
Copyright 2023 The Sigstore Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
const util_1 = require("util");
|
|
const error_1 = require("./error");
|
|
const key_1 = require("./key");
|
|
const policy_1 = require("./policy");
|
|
const timestamp_1 = require("./timestamp");
|
|
const tlog_1 = require("./tlog");
|
|
class Verifier {
|
|
trustMaterial;
|
|
options;
|
|
constructor(trustMaterial, options = {}) {
|
|
this.trustMaterial = trustMaterial;
|
|
this.options = {
|
|
ctlogThreshold: options.ctlogThreshold ?? 1,
|
|
tlogThreshold: options.tlogThreshold ?? 1,
|
|
timestampThreshold: options.timestampThreshold ?? options.tsaThreshold ?? 1,
|
|
tsaThreshold: 0,
|
|
};
|
|
}
|
|
verify(entity, policy) {
|
|
const timestamps = this.verifyTimestamps(entity);
|
|
const signer = this.verifySigningKey(entity, timestamps);
|
|
this.verifyTLogs(entity);
|
|
this.verifySignature(entity, signer);
|
|
if (policy) {
|
|
this.verifyPolicy(policy, signer.identity || {});
|
|
}
|
|
return signer;
|
|
}
|
|
// Checks that all of the timestamps in the entity are valid and returns them
|
|
verifyTimestamps(entity) {
|
|
let timestampCount = 0;
|
|
const timestamps = entity.timestamps.map((timestamp) => {
|
|
switch (timestamp.$case) {
|
|
case 'timestamp-authority':
|
|
timestampCount++;
|
|
return (0, timestamp_1.getTSATimestamp)(timestamp.timestamp, entity.signature.signature, this.trustMaterial.timestampAuthorities);
|
|
case 'transparency-log':
|
|
timestampCount++;
|
|
return (0, timestamp_1.getTLogTimestamp)(timestamp.tlogEntry);
|
|
}
|
|
});
|
|
// Check for duplicate timestamps
|
|
if (containsDupes(timestamps)) {
|
|
throw new error_1.VerificationError({
|
|
code: 'TIMESTAMP_ERROR',
|
|
message: 'duplicate timestamp',
|
|
});
|
|
}
|
|
if (timestampCount < this.options.timestampThreshold) {
|
|
throw new error_1.VerificationError({
|
|
code: 'TIMESTAMP_ERROR',
|
|
message: `expected ${this.options.timestampThreshold} timestamps, got ${timestampCount}`,
|
|
});
|
|
}
|
|
return timestamps.map((t) => t.timestamp);
|
|
}
|
|
// Checks that the signing key is valid for all of the the supplied timestamps
|
|
// and returns the signer.
|
|
verifySigningKey({ key }, timestamps) {
|
|
switch (key.$case) {
|
|
case 'public-key': {
|
|
return (0, key_1.verifyPublicKey)(key.hint, timestamps, this.trustMaterial);
|
|
}
|
|
case 'certificate': {
|
|
const result = (0, key_1.verifyCertificate)(key.certificate, timestamps, this.trustMaterial);
|
|
/* istanbul ignore next - no fixture */
|
|
if (containsDupes(result.scts)) {
|
|
throw new error_1.VerificationError({
|
|
code: 'CERTIFICATE_ERROR',
|
|
message: 'duplicate SCT',
|
|
});
|
|
}
|
|
if (result.scts.length < this.options.ctlogThreshold) {
|
|
throw new error_1.VerificationError({
|
|
code: 'CERTIFICATE_ERROR',
|
|
message: `expected ${this.options.ctlogThreshold} SCTs, got ${result.scts.length}`,
|
|
});
|
|
}
|
|
return result.signer;
|
|
}
|
|
}
|
|
}
|
|
// Checks that the tlog entries are valid for the supplied content
|
|
verifyTLogs({ signature: content, tlogEntries }) {
|
|
let tlogCount = 0;
|
|
tlogEntries.forEach((entry) => {
|
|
tlogCount++;
|
|
(0, tlog_1.verifyTLogInclusion)(entry, this.trustMaterial.tlogs);
|
|
(0, tlog_1.verifyTLogBody)(entry, content);
|
|
});
|
|
if (tlogCount < this.options.tlogThreshold) {
|
|
throw new error_1.VerificationError({
|
|
code: 'TLOG_ERROR',
|
|
message: `expected ${this.options.tlogThreshold} tlog entries, got ${tlogCount}`,
|
|
});
|
|
}
|
|
}
|
|
// Checks that the signature is valid for the supplied content
|
|
verifySignature(entity, signer) {
|
|
if (!entity.signature.verifySignature(signer.key)) {
|
|
throw new error_1.VerificationError({
|
|
code: 'SIGNATURE_ERROR',
|
|
message: 'signature verification failed',
|
|
});
|
|
}
|
|
}
|
|
verifyPolicy(policy, identity) {
|
|
// Check the subject alternative name of the signer matches the policy
|
|
/* istanbul ignore else */
|
|
if (policy.subjectAlternativeName) {
|
|
(0, policy_1.verifySubjectAlternativeName)(policy.subjectAlternativeName, identity.subjectAlternativeName);
|
|
}
|
|
// Check that the extensions of the signer match the policy
|
|
/* istanbul ignore else */
|
|
if (policy.extensions) {
|
|
(0, policy_1.verifyExtensions)(policy.extensions, identity.extensions);
|
|
}
|
|
}
|
|
}
|
|
exports.Verifier = Verifier;
|
|
// Checks for duplicate items in the array. Objects are compared using
|
|
// deep equality.
|
|
function containsDupes(arr) {
|
|
for (let i = 0; i < arr.length; i++) {
|
|
for (let j = i + 1; j < arr.length; j++) {
|
|
if ((0, util_1.isDeepStrictEqual)(arr[i], arr[j])) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|