fix(gist-export): validate OAuth2 state and redirect URI

The state of the OAuth2 response was not verified.
In theory an attacker could use this to extract the contents of a note.

Co-authored-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson
2026-06-17 21:50:50 +02:00
parent dd40a6c04b
commit fbd7307f16
2 changed files with 65 additions and 57 deletions

View File

@@ -65,14 +65,19 @@ function githubActions (req, res, next) {
function githubActionGist (req, res, note) { function githubActionGist (req, res, note) {
const code = req.query.code const code = req.query.code
const state = req.query.state const state = req.query.state
if (!code || !state) { const sessionsState = req.session.githubGistState
const redirectUri = req.session.githubGistRedirectUri
delete req.session.githubGistState
delete req.session.githubGistRedirectUri
if (!code || !state || sessionsState !== state) {
return errors.errorForbidden(res) return errors.errorForbidden(res)
} else { }
const data = { const data = {
client_id: config.github.clientID, client_id: config.github.clientID,
client_secret: config.github.clientSecret, client_secret: config.github.clientSecret,
code, code,
state redirect_uri: redirectUri
} }
const authUrl = 'https://github.com/login/oauth/access_token' const authUrl = 'https://github.com/login/oauth/access_token'
fetch(authUrl, { fetch(authUrl, {
@@ -127,7 +132,6 @@ function githubActionGist (req, res, note) {
logger.error('GitHub Gist auth failed: ' + error) logger.error('GitHub Gist auth failed: ' + error)
return errors.errorInternalError(res) return errors.errorInternalError(res)
}) })
}
} }
function gitlabActions (req, res, next) { function gitlabActions (req, res, next) {

View File

@@ -32,11 +32,15 @@ exports.getInfo = function getInfo (req, res, note) {
} }
exports.createGist = function createGist (req, res, note) { exports.createGist = function createGist (req, res, note) {
const state = nanoid.nanoid()
const redirectUri = config.serverURL + '/auth/github/callback/' + models.Note.encodeNoteId(note.id) + '/gist'
req.session.githubGistState = state
req.session.githubGistRedirectUri = redirectUri
const data = { const data = {
client_id: config.github.clientID, client_id: config.github.clientID,
redirect_uri: config.serverURL + '/auth/github/callback/' + models.Note.encodeNoteId(note.id) + '/gist', redirect_uri: redirectUri,
scope: 'gist', scope: 'gist',
state: nanoid.nanoid() state
} }
const query = querystring.stringify(data) const query = querystring.stringify(data)
res.redirect('https://github.com/login/oauth/authorize?' + query) res.redirect('https://github.com/login/oauth/authorize?' + query)