Skip to content

Commit

Permalink
Apply and follow typed linting rules
Browse files Browse the repository at this point in the history
  • Loading branch information
textbook committed Nov 1, 2024
1 parent afda6cd commit 1c5ed19
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 16 deletions.
30 changes: 28 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import tsEslint from "typescript-eslint";

const vitestEslintRecommended = vitestEslint.configs.recommended;

/** @type {import("eslint").Linter.Config} */
/** @type {import("eslint").Linter.Config[]} */
export default [
{
ignores: ["lib/"],
Expand All @@ -17,12 +17,38 @@ export default [
cyfConfig,
...tsEslint.configs.strict,
...tsEslint.configs.stylistic,
...typeScriptOnly(
tsEslint.configs.strictTypeCheckedOnly,
tsEslint.configs.stylisticTypeCheckedOnly,
),
{
files: ["src/**/*.test.ts"],
files: ["setupTests.ts", "src/**/*.test.ts"],
...vitestEslintRecommended,
rules: {
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
...vitestEslintRecommended.rules,
},
},
];

/**
* Apply the supplied configurations only to TypeScript files.
*
* @param {import("eslint").Linter.Config[][]} configs
* @returns {import("eslint").Linter.Config[]}
*/
function typeScriptOnly(...configs) {
return configs.flat().map((config) => ({
...config,
files: [...(config.files ?? []), "**/*.ts"],
languageOptions: {
...config.languageOptions,
parserOptions: {
...config.languageOptions?.parserOptions,
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
}));
}
2 changes: 1 addition & 1 deletion src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const validatePayload = async (body: string, signature: string): Promise<
if (!(await verify(secret, body, signature))) {
throw new Error("payload validation failed");
}
const payload: PingEvent | RepositoryEvent = JSON.parse(body);
const payload = JSON.parse(body) as PingEvent | RepositoryEvent;
if (!("action" in payload) || payload.action !== "created") {
console.log(`Ignoring event: ${"action" in payload ? payload.action : "ping"}`);
return;
Expand Down
4 changes: 2 additions & 2 deletions src/netlify/slack_interaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,12 @@ describe("slack interaction handler", () => {
const timestamp = Math.floor(Date.now() / 1_000);
const body = new URLSearchParams(payload).toString();
const hmac = createHmac("sha256", secret);
hmac.update(`v0:${timestamp}:${body}`);
hmac.update(`v0:${timestamp.toFixed(0)}:${body}`);
const signature = hmac.digest("hex");
return { body, signature, timestamp };
};

const makeRequest = (body: string, signature: string, timestamp: number): Promise<HandlerResponse> => {
return handler({ body, headers: { "x-slack-request-timestamp": `${timestamp}`, "x-slack-signature": `v0=${signature}` } });
return handler({ body, headers: { "x-slack-request-timestamp": timestamp.toFixed(0), "x-slack-signature": `v0=${signature}` } });
};
});
14 changes: 7 additions & 7 deletions src/slack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import { validatePayload } from "./slack.js";
import type { Maybe, MessageRef } from "./types.js";

describe("validatePayload", () => {
it("rejects invalid version", async () => {
it("rejects invalid version", () => {
expect(() => attemptValidation({
signature: "v1=abc123",
})).toThrow("invalid signature version");
});

it("rejects old timestamp", async () => {
it("rejects old timestamp", () => {
expect(() => attemptValidation({
signature: "v0=abc123",
timestamp: Math.floor((Date.now() / 1_000) - (6 * 60)),
})).toThrow("timestamp too old");
});

it("rejects invalid hash", async () => {
it("rejects invalid hash", () => {
const body = "goodbye=world";
process.env.SLACK_SIGNING_SECRET = "keepitquiet";
const { signature, timestamp } = sign(body, "someothersecret");
Expand All @@ -28,7 +28,7 @@ describe("validatePayload", () => {
})).toThrow("payload validation failed");
});

it("turns the body into an object", async () => {
it("turns the body into an object", () => {
const body = `hello=world&payload=${JSON.stringify({})}`;
const secret = "secretsquirrel";
process.env.SLACK_SIGNING_SECRET = secret;
Expand All @@ -40,7 +40,7 @@ describe("validatePayload", () => {
})).toBeUndefined();
});

it("extracts the relevant delete action", async () => {
it("extracts the relevant delete action", () => {
const payload = {
actions: [{
action_id: "delete-repository",
Expand Down Expand Up @@ -71,7 +71,7 @@ describe("validatePayload", () => {
});
});

it("extracts the relevant dismiss action", async () => {
it("extracts the relevant dismiss action", () => {
const payload = {
actions: [{
action_id: "dismiss-deletion",
Expand Down Expand Up @@ -114,6 +114,6 @@ describe("validatePayload", () => {
const sign = (payload: string, secret: string): { signature: string, timestamp: number } => {
const timestamp = Math.floor(Date.now() / 1_000);
const hmac = createHmac("sha256", secret);
hmac.update(`v0:${timestamp}:${payload}`);
hmac.update(`v0:${timestamp.toFixed(0)}:${payload}`);
return { signature: `v0=${hmac.digest("hex")}`, timestamp };
};
9 changes: 5 additions & 4 deletions src/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ export const validatePayload = (body: string, signature: string, timestamp: numb
if (!isValid(body, signature, timestamp)) {
throw new Error("payload validation failed");
}
return getPayload(JSON.parse(Object.fromEntries(new URLSearchParams(body).entries()).payload));
const payload = JSON.parse(Object.fromEntries(new URLSearchParams(body).entries()).payload) as SlackInteraction;
return getPayload(payload);
};

const actionsSection = (repo: Repository): ActionsBlock => ({
Expand Down Expand Up @@ -120,8 +121,8 @@ const fiveMinutesAgo = () => Math.floor((Date.now() / 1_000) - (5 * 60));

const getPayload = ({ actions = [], message, user }: SlackInteraction): Maybe<MessageRef> => {
const action = actions.find(({ action_id }) => [DELETE_ACTION_ID, DISMISS_ACTION_ID].includes(action_id ?? ""));
if (action && action.value) {
const repo: Repository = JSON.parse(action.value);
if (action?.value) {
const repo = JSON.parse(action.value) as Repository;
return {
action: action.action_id === DELETE_ACTION_ID ? "delete" : "dismiss",
messageTs: message.ts,
Expand Down Expand Up @@ -153,7 +154,7 @@ const repoSection = ({ repoName, repoUrl, userLogin, userName, userUrl }: Reposi
const lines = [
`A new repository <${repoUrl}|\`${repoName}\`> was just created by <${userUrl}|${userName ? userName : `\`${userLogin}\``}>.`,
];
const match = repoUrl.match(/-\d+$/);
const match = /-\d+$/.exec(repoUrl);
if (match !== null) {
lines.push(`:redflag: *The \`${match[0]}\` makes this likely a mistake.*`);
}
Expand Down

0 comments on commit 1c5ed19

Please sign in to comment.