Repository navigation
fix(oauth): forbid framing of the consent and setup pages and show where approval sends access #747
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
Merged
+616
−88
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
d1ce461
fix(oauth): forbid framing and set security headers on every response
aliasunder 14d9ea1
fix(oauth): stop browsers caching consent decisions and setup pages
aliasunder e52949d
fix(oauth): show where approval sends access and grant every sign-in …
aliasunder 63f79e0
docs: describe the consent page's destination row, the granted scope …
aliasunder b7cbbbb
test: use neutral example hosts in the consent page destination vectors
aliasunder 57aec84
docs: give the response headers their own SECURITY.md section
aliasunder 49ce00a
docs: name the server and setup mode in the response headers section
aliasunder fdb9e03
fix(review): describe the consent decision's parser-error response ac…
aliasunder 731b86d
style: clarify the response-header, consent-page and setup-server com…
aliasunder 2ad0bd6
refactor(oauth): let the consent page own its name fallback, and name…
aliasunder 85acbcc
test: name each caching and header test for the behaviour it checks, …
aliasunder 5e7e8dd
test: read the consent page's reveal toggle without non-null assertions
aliasunder 3c08e20
fix(docs): say that Express's 404 page answers any request no route h…
aliasunder db36121
fix(oauth): show the client ID when a client's registered name is empty
aliasunder 1394d15
refactor(oauth): name the only scope VAULT_SCOPE, and reflow the scop…
aliasunder 2e5198b
test: pin no-store on the consent page, and say which layer answers a…
aliasunder a1bf47d
style: say an empty client name also shows the client ID, and give th…
aliasunder 4bd0391
test: pin the full server's 500 for an oversized consent form as not …
aliasunder File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { describe, it, expect, onTestFinished } from "vitest" | ||
| import type { Server } from "node:http" | ||
| import express from "express" | ||
| import { securityHeaders } from "../security-headers.js" | ||
|
|
||
| const startApp = async (): Promise<string> => { | ||
| const app = express() | ||
| app.use(securityHeaders) | ||
| app.get("/page", (_req, res) => { | ||
| res.send("page body") | ||
| }) | ||
| const server = await new Promise<Server>((resolve) => { | ||
| const listening = app.listen(0, "127.0.0.1", () => resolve(listening)) | ||
| }) | ||
| onTestFinished(() => new Promise<void>((resolve) => server.close(() => resolve()))) | ||
| const serverAddress = server.address() | ||
|
|
||
| if (!serverAddress || typeof serverAddress === "string") { | ||
| throw new Error("expected a TCP address from a listening server") | ||
| } | ||
| return `http://127.0.0.1:${serverAddress.port}` | ||
| } | ||
|
|
||
| describe("securityHeaders", () => { | ||
| it("sets the framing, sniffing and referrer headers, then hands the request on", async () => { | ||
| const baseUrl = await startApp() | ||
|
|
||
| const response = await fetch(`${baseUrl}/page`) | ||
|
|
||
| expect({ | ||
| frameOptions: response.headers.get("x-frame-options"), | ||
| contentSecurityPolicy: response.headers.get("content-security-policy"), | ||
| contentTypeOptions: response.headers.get("x-content-type-options"), | ||
| referrerPolicy: response.headers.get("referrer-policy"), | ||
| }).toEqual({ | ||
| frameOptions: "DENY", | ||
| contentSecurityPolicy: "frame-ancestors 'none'", | ||
| contentTypeOptions: "nosniff", | ||
| referrerPolicy: "same-origin", | ||
| }) | ||
| expect(response.status).toBe(200) | ||
| await expect(response.text()).resolves.toBe("page body") | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import type { NextFunction, Request, Response } from "express" | ||
|
|
||
| const SECURITY_HEADERS = { | ||
| // Browsers that predate the frame-ancestors directive read only this header. | ||
| "X-Frame-Options": "DENY", | ||
| // The policy names only frame-ancestors, because a fuller one would block the | ||
| // consent and setup pages' inline script and styles. | ||
| "Content-Security-Policy": "frame-ancestors 'none'", | ||
| "X-Content-Type-Options": "nosniff", | ||
| // Sends no referrer to other sites. no-referrer would also make the consent | ||
| // form's post arrive with `Origin: null`, hiding which site sent it. | ||
| "Referrer-Policy": "same-origin", | ||
| } | ||
|
|
||
| /** Express middleware that stops other sites from framing a response and from | ||
| * receiving its address as a referrer, and turns off content-type sniffing. */ | ||
| export const securityHeaders = (_req: Request, res: Response, next: NextFunction): void => { | ||
| res.set(SECURITY_HEADERS) | ||
| next() | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.