Skip to content

Commit

Permalink
some refactoring to get the output matrix right.
Browse files Browse the repository at this point in the history
  • Loading branch information
argl committed Jun 28, 2024
1 parent bebf9a4 commit a2693ef
Show file tree
Hide file tree
Showing 22 changed files with 289 additions and 83 deletions.
21 changes: 17 additions & 4 deletions src/analyzer/tests/cookies.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Requests, Expectation, BaseOutput } from "../../types.js";
import { Requests, BaseOutput } from "../../types.js";
import { Expectation } from "../../types.js";
import { onlyIfWorse } from "../utils.js";
import { strictTransportSecurityTest } from "./strict-transport-security.js";
import { CookieJar, Cookie } from "tough-cookie";
import { Cookie } from "tough-cookie";

// See: https://github.com/mozilla/http-observatory/issues/282 for the heroku-session-affinity insanity
const COOKIES_TO_DELETE = ["heroku-session-affinity"];
Expand All @@ -28,12 +29,24 @@ export class CookiesOutput extends BaseOutput {
// Store whether or not we saw SameSite cookies, if cookies were set
/** @type {boolean | null} */
sameSite = null;
static name = "cookies";
static title = "Cookies";
static possibleResults = [
Expectation.CookiesSecureWithHttponlySessionsAndSamesite,
Expectation.CookiesSecureWithHttponlySessions,
Expectation.CookiesNotFound,
Expectation.CookiesWithoutSecureFlagButProtectedByHsts,
Expectation.CookiesSessionWithoutSecureFlagButProtectedByHsts,
Expectation.CookiesWithoutSecureFlag,
Expectation.CookiesSamesiteFlagInvalid,
Expectation.CookiesAnticsrfWithoutSamesiteFlag,
Expectation.CookiesSessionWithoutHttponlyFlag,
Expectation.CookiesSessionWithoutSecureFlag,
];

/** @param {Expectation} expectation */
constructor(expectation) {
super(expectation);
this.name = "cookies";
this.title = "Cookies";
}
}

Expand Down
13 changes: 10 additions & 3 deletions src/analyzer/tests/cors.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { BaseOutput, Expectation, Requests } from "../../types.js";
import { BaseOutput, Requests } from "../../types.js";
import { Expectation } from "../../types.js";

export class CorsOutput extends BaseOutput {
/** @type {string | null} */
data = null;
static name = "cross-origin-resource-sharing";
static title = "Cross Origin Resource Sharing (CORS)";
static possibleResults = [
Expectation.CrossOriginResourceSharingNotImplemented,
Expectation.CrossOriginResourceSharingImplementedWithPublicAccess,
Expectation.CrossOriginResourceSharingImplementedWithRestrictedAccess,
Expectation.CrossOriginResourceSharingImplementedWithUniversalAccess,
];

/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "cross-origin-resource-sharing";
this.title = "Cross Origin Resource Sharing (CORS)";
}
}

Expand Down
14 changes: 11 additions & 3 deletions src/analyzer/tests/cross-origin-resource-policy.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { BaseOutput, Expectation, Requests } from "../../types.js";
import { BaseOutput, Requests } from "../../types.js";
import { Expectation } from "../../types.js";

export class CrossOriginResourcePolicyOutput extends BaseOutput {
/** @type {string | null} */
data = null;
http = false;
meta = false;
static name = "cross-origin-resource-policy";
static title = "Cross Origin Resource Policy";
static possibleResults = [
Expectation.CrossOriginResourcePolicyNotImplemented,
Expectation.CrossOriginResourcePolicyImplementedWithSameOrigin,
Expectation.CrossOriginResourcePolicyImplementedWithSameSite,
Expectation.CrossOriginResourcePolicyImplementedWithCrossOrigin,
Expectation.CrossOriginResourcePolicyHeaderInvalid,
];

/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "cross-origin-resource-policy";
this.title = "Cross Origin Resource Policy";
}
}

Expand Down
19 changes: 15 additions & 4 deletions src/analyzer/tests/csp.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CONTENT_SECURITY_POLICY } from "../../headers.js";
import { Requests, Expectation, Policy, BaseOutput } from "../../types.js";
import { Requests, Policy, BaseOutput } from "../../types.js";
import { Expectation } from "../../types.js";
import { parseCsp } from "../cspParser.js";

const DANGEROUSLY_BROAD = new Set([
Expand Down Expand Up @@ -32,15 +33,25 @@ export class CspOutput extends BaseOutput {
/** @type {Policy | null} */
policy = null;
numPolicies = 0;

static name = "content-security-policy";
static title = "Content Security Policy (CSP)";
static possibleResults = [
Expectation.CspImplementedWithNoUnsafeDefaultSrcNone,
Expectation.CspImplementedWithNoUnsafe,
Expectation.CspImplementedWithUnsafeInlineInStyleSrcOnly,
Expectation.CspImplementedWithInsecureSchemeInPassiveContentOnly,
Expectation.CspImplementedWithUnsafeEval,
Expectation.CspImplementedWithUnsafeInline,
Expectation.CspImplementedWithInsecureScheme,
Expectation.CspHeaderInvalid,
Expectation.CspNotImplemented,
];
/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "content-security-policy";
this.title = "Content Security Policy (CSP)";
}
}

Expand Down
17 changes: 14 additions & 3 deletions src/analyzer/tests/redirection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BaseOutput, Expectation, Requests } from "../../types.js";
import { BaseOutput, Requests } from "../../types.js";
import { Expectation } from "../../types.js";
import { isHstsPreloaded } from "../hsts.js";

export class RedirectionOutput extends BaseOutput {
Expand All @@ -9,15 +10,25 @@ export class RedirectionOutput extends BaseOutput {
route = [];
/** @type {number | null} */
statusCode = null;
static name = "redirection";
static title = "Redirection";
static possibleResults = [
Expectation.RedirectionAllRedirectsPreloaded,
Expectation.RedirectionToHttps,
Expectation.RedirectionNotNeededNoHttp,
Expectation.RedirectionOffHostFromHttp,
Expectation.RedirectionNotToHttpsOnInitialRedirection,
Expectation.RedirectionNotToHttps,
Expectation.RedirectionMissing,
Expectation.RedirectionInvalidCert,
];

/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "redirection";
this.title = "Redirection";
}
}

Expand Down
13 changes: 10 additions & 3 deletions src/analyzer/tests/referrer-policy.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { Requests, Expectation, BaseOutput } from "../../types.js";
import { Requests, BaseOutput } from "../../types.js";
import { Expectation } from "../../types.js";
export class ReferrerOutput extends BaseOutput {
/** @type {string | null} */
data = null;
http = false;
meta = false;
static name = "referrer-policy";
static title = "Referrer Policy";
static possibleResults = [
Expectation.ReferrerPolicyPrivate,
Expectation.ReferrerPolicyNotImplemented,
Expectation.ReferrerPolicyUnsafe,
Expectation.ReferrerPolicyHeaderInvalid,
];

/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "referrer-policy";
this.title = "Referrer Policy";
}
}

Expand Down
17 changes: 13 additions & 4 deletions src/analyzer/tests/strict-transport-security.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Requests, Expectation, BaseOutput } from "../../types.js";
import { Requests, BaseOutput } from "../../types.js";
import { Expectation } from "../../types.js";
import { isHstsPreloaded } from "../hsts.js";
export class StrictTransportSecurityOutput extends BaseOutput {
/** @type {string | null} */
Expand All @@ -8,15 +9,23 @@ export class StrictTransportSecurityOutput extends BaseOutput {
maxAge = null;
preload = false;
preloaded = false;

static name = "strict-transport-security";
static title = "Strict Transport Security (HSTS)";
static possibleResults = [
Expectation.HstsPreloaded,
Expectation.HstsImplementedMaxAgeAtLeastSixMonths,
Expectation.HstsImplementedMaxAgeLessThanSixMonths,
Expectation.HstsNotImplemented,
Expectation.HstsHeaderInvalid,
Expectation.HstsNotImplementedNoHttps,
Expectation.HstsInvalidCert,
];
/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "strict-transport-security";
this.title = "Strict Transport Security (HSTS)";
}
}

Expand Down
17 changes: 14 additions & 3 deletions src/analyzer/tests/subresource-integrity.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { BaseOutput, Expectation, HTML_TYPES, Requests } from "../../types.js";
import { BaseOutput, HTML_TYPES, Requests } from "../../types.js";
import { Expectation } from "../../types.js";
import { JSDOM } from "jsdom";
import { parse } from "tldts";
import { onlyIfWorse } from "../utils.js";

export class SubresourceIntegrityOutput extends BaseOutput {
/** @type {import("../../types.js").ScriptMap} */
data;
static name = "subresource-integrity";
static title = "Subresource Integrity";
static possibleResults = [
Expectation.SriImplementedAndAllScriptsLoadedSecurely,
Expectation.SriImplementedAndExternalScriptsLoadedSecurely,
Expectation.SriNotImplementedResponseNotHtml,
Expectation.SriNotImplementedButNoScriptsLoaded,
Expectation.SriNotImplementedButAllScriptsLoadedFromSecureOrigin,
Expectation.SriNotImplementedButExternalScriptsLoadedSecurely,
Expectation.SriImplementedButExternalScriptsNotLoadedSecurely,
Expectation.SriNotImplementedAndExternalScriptsNotLoadedSecurely,
];

/**
*
Expand All @@ -14,8 +27,6 @@ export class SubresourceIntegrityOutput extends BaseOutput {
constructor(expectation) {
super(expectation);
this.data = {};
this.name = "subresource-integrity";
this.title = "Subresource Integrity";
}
}

Expand Down
12 changes: 9 additions & 3 deletions src/analyzer/tests/x-content-type-options.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { BaseOutput, Expectation, Requests } from "../../types.js";
import { BaseOutput, Requests } from "../../types.js";
import { Expectation } from "../../types.js";

export class XContentTypeOptionsOutput extends BaseOutput {
/** @type {string | null} */
data = null;
static name = "x-content-type-options";
static title = "X-Content-Type-Options";
static possibleResults = [
Expectation.XContentTypeOptionsNosniff,
Expectation.XContentTypeOptionsHeaderInvalid,
Expectation.XContentTypeOptionsNotImplemented,
];

/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "x-content-type-options";
this.title = "X-Content-Type-Options";
}
}

Expand Down
14 changes: 11 additions & 3 deletions src/analyzer/tests/x-frame-options.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import { BaseOutput, Expectation, Requests } from "../../types.js";
import { BaseOutput, Requests } from "../../types.js";
import { Expectation } from "../../types.js";
import { contentSecurityPolicyTest } from "./csp.js";

export class XFrameOptionsOutput extends BaseOutput {
/** @type {string | null} */
data = null;
static name = "x-frame-options";
static title = "X-Frame-Options";
static possibleResults = [
Expectation.XFrameOptionsImplementedViaCsp,
Expectation.XFrameOptionsSameoriginOrDeny,
Expectation.XFrameOptionsAllowFromOrigin,
Expectation.XFrameOptionsNotImplemented,
Expectation.XFrameOptionsHeaderInvalid,
];

/**
*
* @param {Expectation} expectation
*/
constructor(expectation) {
super(expectation);
this.name = "x-frame-options";
this.title = "X-Frame-Options";
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import helmet from "@fastify/helmet";
import analyzeApiV2 from "./v2/analyze/index.js";
import scanApiV2 from "./v2/scan/index.js";
import statsApiV2 from "./v2/stats/index.js";
import recommendationMatrixApiV2 from "./v2/recommendations/index.js";
import globalErrorHandler from "./global-error-handler.js";
import pool from "@fastify/postgres";
import { poolOptions } from "../database/repository.js";
Expand Down Expand Up @@ -62,6 +63,7 @@ export async function createServer() {
server.register(analyzeApiV2, { prefix: "/api/v2" }),
server.register(scanApiV2, { prefix: "/api/v2" }),
server.register(statsApiV2, { prefix: "/api/v2" }),
server.register(recommendationMatrixApiV2, { prefix: "/api/v2" }),
]);

["SIGINT", "SIGTERM"].forEach((signal) => {
Expand Down
44 changes: 44 additions & 0 deletions src/api/v2/recommendations/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { CookiesOutput } from "../../../analyzer/tests/cookies.js";
import { ALL_RESULTS, ALL_TESTS } from "../../../constants.js";
import { SCORE_TABLE, TEST_TOPIC_LINKS } from "../../../grader/charts.js";
import { SCHEMAS } from "../schemas.js";

/**
* Register the API - default export
* @param {import('fastify').FastifyInstance} fastify
* @returns {Promise<void>}
*/
export default async function (fastify) {
const pool = fastify.pg.pool;

fastify.get(
"/recommendation_matrix",
{ schema: SCHEMAS.recommendationMatrix },
async (request, reply) => {
const res = ALL_RESULTS.map((output) => {
return {
name: output.name,
title: output.title,
mdnLink: TEST_TOPIC_LINKS.get(output.name) || "",
results: output.possibleResults.map((pr) => {
const data = SCORE_TABLE.get(pr);
return data
? {
name: pr,
scoreModifier: data.modifier,
description: data.description,
recommendation: data.recommendation,
}
: {
name: pr,
scoreModifier: 0,
description: "",
recommendation: "",
};
}),
};
});
return res;
}
);
}
Loading

0 comments on commit a2693ef

Please sign in to comment.