Skip to content

Commit

Permalink
improve CSP usage tracker (#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
quisido authored Jul 4, 2024
1 parent 894d963 commit 08cdc21
Show file tree
Hide file tree
Showing 55 changed files with 849 additions and 274 deletions.
4 changes: 1 addition & 3 deletions packages/authn-shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@
"vitest:run": "vitest run",
"vitest:watch": "vitest watch"
},
"dependencies": {
"fmrs": "workspace:^"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.15.3",
"@babel/core": "^7.24.7",
Expand All @@ -54,6 +51,7 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"fmrs": "workspace:^",
"prettier": "^3.3.2",
"tslib": "^2.6.3",
"typescript": "~5.4.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/authn/src/utils/is-static-pathname.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { StaticPathname } from '../constants/static-pathname.js';

const PATHNAMES: Set<unknown> = new Set<unknown>(Object.values(StaticPathname));

export default function isReservedPathname(
export default function isStaticPathname(
value: unknown,
): value is StaticPathname {
return PATHNAMES.has(value);
Expand Down
5 changes: 5 additions & 0 deletions packages/csp-shared/.attw.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"format": "table-flipped",
"ignoreRules": ["cjs-resolves-to-esm"],
"pack": "."
}
8 changes: 8 additions & 0 deletions packages/csp-shared/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/coverage/
/dist/
/node_modules/
/.eslintcache
/*.cpuprofile
/*.iml
/*.tsbuildinfo
/*-*.*.*.tgz
15 changes: 15 additions & 0 deletions packages/csp-shared/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/.vscode/
/coverage/
/node_modules/
/src/
/.*.json
/.*ignore
/.*rc.*
/.eslintcache
/*.config.*
/*.cpuprofile
/*.iml
/*.tsbuildinfo
/*-*.*.*.tgz
/tsconfig.*
.DS_Store
12 changes: 12 additions & 0 deletions packages/csp-shared/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"files.exclude": {
"coverage/": true,
"dist/": true,
"node_modules/": true,
".attw.json": true,
".eslintcache": true,
"*.cpuprofile": true,
"*.tsbuildinfo": true,
"vitest.config.ts.timestamp-*-*.mjs": true
}
}
21 changes: 21 additions & 0 deletions packages/csp-shared/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 https://quisi.do/

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
4 changes: 4 additions & 0 deletions packages/csp-shared/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# [quisi.do](https://quisi.do/) authentication

This module contains isomorphic JavaScript for the [quisi.do](https://quisi.do/)
authentication service.
13 changes: 13 additions & 0 deletions packages/csp-shared/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import configs from '@quisido/eslint-config';

/** @type {readonly import('eslint').Linter.FlatConfig[]} */
export default [
...configs,

{
files: ['src/get-error-code.ts'],
rules: {
'no-magic-numbers': 'off',
},
},
];
61 changes: 61 additions & 0 deletions packages/csp-shared/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"name": "@quisido/csp-shared",
"version": "1.0.0",
"author": "quisi.do <[email protected]>",
"description": "isomorphic Content Security Policy modules",
"homepage": "https://github.com/quisido/quisi.do/tree/main/packages/csp-shared#readme",
"license": "MIT",
"main": "./dist/index.js",
"repository": "github:quisido/quisi.do",
"type": "module",
"types": "./dist/index.d.ts",
"bugs": {
"email": "[email protected]",
"url": "https://github.com/quisido/quisi.do/issues"
},
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/quisido"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"attw": "attw --quiet",
"clean": "rm -rf coverage dist && rm -f tsconfig.prepack.tsbuildinfo tsc-output.cpuprofile",
"eslint": "eslint . --cache --color --exit-on-fatal-error --max-warnings 0",
"eslint:fix": "eslint . --cache --color --exit-on-fatal-error --fix --max-warnings 0",
"prepack": "yarn run tsc",
"prepublish": "concurrently --kill-others-on-fail --names attw,eslint,vitest \"yarn run attw\" \"yarn run eslint\" \"yarn run vitest:run\"",
"tsc": "tsc --generateCpuProfile tsc-output.cpuprofile --project tsconfig.prepack.json",
"vitest": "vitest",
"vitest:run": "vitest run",
"vitest:watch": "vitest watch"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.15.3",
"@babel/core": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
"@quisido/eslint-config": "workspace:^",
"@quisido/vitest-config": "workspace:^",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"@vitest/coverage-istanbul": "^1.6.0",
"babel-plugin-replace-import-extension": "^1.1.4",
"cjs-ts": "workspace:^",
"concurrently": "^8.2.2",
"eslint": "^9.5.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"fmrs": "workspace:^",
"prettier": "^3.3.2",
"tslib": "^2.6.3",
"typescript": "~5.4.5",
"vite": "^5.3.2",
"vitest": "^1.6.0"
}
}
17 changes: 17 additions & 0 deletions packages/csp-shared/src/get-error-code.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { isNumber, sortUnknown } from 'fmrs';
import { describe, expect, it } from 'vitest';
import { GetErrorCode } from './index.js';
import createAscendingArray from './test/create-ascending-array.js';

const GET_ERROR_CODES: readonly number[] =
Object.values(GetErrorCode).filter(isNumber);

const ASCENDING_ARRAY: readonly number[] = createAscendingArray(
GET_ERROR_CODES.length,
);

describe('ErrorCode', (): void => {
it('should contain ascending values', (): void => {
expect([...GET_ERROR_CODES].sort(sortUnknown)).toEqual(ASCENDING_ARRAY);
});
});
5 changes: 5 additions & 0 deletions packages/csp-shared/src/get-error-code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum GetErrorCode {
InvalidDatabaseProjectRow = 3,
InvalidKey = 2,
MissingKey = 1,
}
1 change: 1 addition & 0 deletions packages/csp-shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GetErrorCode } from './get-error-code.js';
6 changes: 6 additions & 0 deletions packages/csp-shared/src/test/create-ascending-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { mapToIndex } from 'fmrs';
import increment from './increment.js';

export default function createAscendingArray(length: number): number[] {
return new Array(length).fill(null).map(mapToIndex).map(increment);
}
5 changes: 5 additions & 0 deletions packages/csp-shared/src/test/increment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const INCREMENT = 1;

export default function increment(num: number): number {
return num + INCREMENT;
}
11 changes: 11 additions & 0 deletions packages/csp-shared/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"exclude": ["coverage/", "dist/"],
"extends": "../../tsconfig.json",
"include": ["**/*.ts"],
"compilerOptions": {
"lib": ["ESNext"],
"noEmit": true,
"rootDir": ".",
"sourceRoot": "."
}
}
12 changes: 12 additions & 0 deletions packages/csp-shared/tsconfig.prepack.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"exclude": ["src/**/*.test.ts", "src/test/"],
"extends": "./tsconfig.json",
"include": ["src/"],
"compilerOptions": {
"declarationDir": "dist",
"noEmit": false,
"outDir": "dist",
"rootDir": "src",
"sourceRoot": "https://github.com/quisido/quisi.do/raw/main/packages/authn-shared/src/"
}
}
1 change: 1 addition & 0 deletions packages/csp-shared/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@quisido/vitest-config';
7 changes: 7 additions & 0 deletions packages/csp/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ import configs from '@quisido/eslint-config';
export default [
...configs,

{
rules: {
// The `queries` function uses numbers as a generic.
'no-magic-numbers': 'off',
},
},

{
files: ['src/constants/*.ts'],
rules: {
Expand Down
4 changes: 3 additions & 1 deletion packages/csp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
"whoami": "wrangler whoami"
},
"dependencies": {
"@quisido/csp-shared": "workspace:^",
"@quisido/workers-shared": "workspace:^",
"cloudflare-utils": "workspace:^"
"cloudflare-utils": "workspace:^",
"fmrs": "workspace:^"
},
"devDependencies": {
"@babel/core": "^7.24.7",
Expand Down
11 changes: 9 additions & 2 deletions packages/csp/sql/keys.sql
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
CREATE TABLE IF NOT EXISTS [keys] (
"projectId" UNSIGNED INTEGER NOT NULL,
"key" TEXT NOT NULL,
"permission" UNSIGNED INTEGER NOT NULL,
PRIMARY KEY(`key`),
FOREIGN KEY(`projectId`) REFERENCES projects(`projectId`) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_keys_projectId_key
ON keys(`projectId`, `key`);

INSERT INTO `keys` (`projectId`, `key`)
VALUES (1, "demo");
INSERT INTO `keys` (`projectId`, `key`, `permission`)
VALUES (1, "demo-post", 1);

INSERT INTO `keys` (`projectId`, `key`, `permission`)
VALUES (1, "demo-get", 2);

INSERT INTO `keys` (`projectId`, `key`, `permission`)
VALUES (1, "demo-delete", 3);
7 changes: 0 additions & 7 deletions packages/csp/src/constants/default-d1-read-data-point.ts

This file was deleted.

13 changes: 0 additions & 13 deletions packages/csp/src/constants/default-d1-read-doubles.ts

This file was deleted.

18 changes: 18 additions & 0 deletions packages/csp/src/constants/handle-static-pathname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
FAVICON_RESPONSE_BODY,
FAVICON_RESPONSE_INIT,
ROBOTS_RESPONSE_BODY,
ROBOTS_RESPONSE_INIT,
} from '../constants/responses.js';
import { StaticPathname } from '../constants/static-pathname.js';

export default function handleStaticPathname(
pathname: StaticPathname,
): Response {
switch (pathname) {
case StaticPathname.Favicon:
return new Response(FAVICON_RESPONSE_BODY, FAVICON_RESPONSE_INIT);
case StaticPathname.Robots:
return new Response(ROBOTS_RESPONSE_BODY, ROBOTS_RESPONSE_INIT);
}
}
5 changes: 5 additions & 0 deletions packages/csp/src/constants/permission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum Permission {
Delete = 3,
Get = 2,
Post = 1,
}
7 changes: 7 additions & 0 deletions packages/csp/src/constants/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ export const SELECT_ORIGINS_USER_ID_FROM_PROJECTS = `
WHERE \`projectId\` = ?;
`;

export const SELECT_PERMISSION_FROM_KEYS = `
SELECT \`permission\`
FROM \`keys\`
WHERE \`key\` = ?
AND \`projectId\` = ?;
`;

export const SELECT_USER_ID_FROM_PROJECTS = `
SELECT \`userId\`
FROM \`projects\`
Expand Down
38 changes: 38 additions & 0 deletions packages/csp/src/constants/responses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* You can only construct a `Response` during a request, so we must keep these
* parameters deconstructed for now.
*
* https://dev.to/kleeut/
* cloudflare-workers-some-functionality-can-only-
* be-performed-while-handling-a-request-3bne
*/

import { StatusCode } from './status-code.js';

export const FAVICON_RESPONSE_BODY: BodyInit =
'%00%00%01%00%01%00%01%01%00%00%01%00%20%000%00%00%00%16%00%00%00(%00%00%00%01%00%00%00%02%00%00%00%01%00%20%00%00%00%00%00%08%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00';

export const FAVICON_RESPONSE_INIT: ResponseInit = {
status: StatusCode.OK,

headers: new Headers({
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Max-Age': '31536000',
Allow: 'GET, OPTIONS',
'Cache-Control': 'immutable, max-age=31536000, public',
'Content-Type': 'image/x-icon; charset=utf-8',
}),
};

export const ROBOTS_RESPONSE_BODY: BodyInit = 'Disallow: *';
export const ROBOTS_RESPONSE_INIT: ResponseInit = {
status: StatusCode.OK,

headers: new Headers({
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Max-Age': '31536000',
Allow: 'GET, OPTIONS',
'Cache-Control': 'immutable, max-age=31536000, public',
'Content-Type': 'text/plain; charset=utf-8',
}),
};
4 changes: 4 additions & 0 deletions packages/csp/src/constants/static-pathname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum StaticPathname {
Favicon = '/favicon.ico',
Robots = '/robots.txt',
}
Loading

0 comments on commit 08cdc21

Please sign in to comment.