Skip to content

Commit

Permalink
logs: only allow organization members to access them
Browse files Browse the repository at this point in the history
The bot's logs are accessible to everyone at the /logs endpoint. This is
dangerous because the logs may contain sensitive info.

Fix this by only allowing se-edu organization members to view the logs.

Note that our access control doesn't actually check if the current user
is a member of se-edu, but rather whether the user has access to the
se-edu-bot installation in the se-edu organization. This should be
equivalent.
  • Loading branch information
pyokagan committed Jun 15, 2017
1 parent 805a9a7 commit 1eb4f04
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ which provides some useful base functionality such as splitting the `webhookMidd
To facilitate debugging problems in production,
se-edu-bot exposes its logs via the `/logs` endpoint.

You need to be a member of the `se-edu` organization in order to access them.

== Coding standard

We follow the oss-generic coding standard.
Expand Down
44 changes: 44 additions & 0 deletions lib/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import koaCompose = require('koa-compose');
import koaRoute = require('koa-route');
import simpleOauth2 = require('simple-oauth2');
import createError = require('http-errors');
import * as github from '../lib/github';

/**
* Options to pass to {@link Auth}.
Expand Down Expand Up @@ -59,6 +60,44 @@ export class Auth {
]);
}

createAccessControlByInstallationId(installationId: number): Koa.Middleware {
return async (ctx: Koa.Context, next?: () => Promise<void>): Promise<void> => {
const ghUserApi = this.getGhUserApi(ctx);
if (!ghUserApi) {
ctx.redirect(this.getLoginRedirect(ctx));
return;
}

// Check that the user has access to our installation
const installationIds: number[] = [];
await github.forEachPage(ghUserApi, {
url: 'user/installations',
}, body => {
const installations: any[] = body.installations;
installations.forEach(installation => installationIds.push(installation.id));
});

if (!installationIds.includes(installationId)) {
throw createError(403, 'User is not authorized to access this page');
}

if (next) {
await next();
}
};
}

private getGhUserApi(ctx: Koa.Context): github.RequestApi | undefined {
const accessToken = ctx.cookies.get(this.accessTokenCookieName);
if (!accessToken) {
return;
}
return github.createAccessTokenApi({
accessToken,
userAgent: this.userAgent,
});
}

private async loginMiddleware(ctx: Koa.Context): Promise<void> {
const authorizationUri = this.oauth2.authorizationCode.authorizeURL({
redirect_uri: this.getOauthRedirectUri(ctx),
Expand Down Expand Up @@ -112,6 +151,11 @@ export class Auth {
}
return redirect;
}

private getLoginRedirect(ctx: Koa.Context): string {
const redirect = `${ctx.path}${ctx.search}`;
return `${this.loginRoute}?redirect=${encodeURIComponent(redirect)}`;
}
}

export default Auth;
1 change: 1 addition & 0 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export function createApp(appConfig: AppConfig): Koa {
fileName: logFileName,
});
app.use(koaRoute.get('/logs', koaCompose<Koa.Context>([
auth.createAccessControlByInstallationId(appConfig.githubInstallationId),
async ctx => {
ctx.type = 'text';
ctx.body = logger.createReadableStream();
Expand Down

0 comments on commit 1eb4f04

Please sign in to comment.