mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-11-14 00:07:00 +01:00
fix: prevent folder downloads from exposing GitHub metadata
This commit is contained in:
1 parent
4834289c8e
commit
7c7caa845c
6 files changed
+115
-8
No files matched your search
@@ -362,7 +362,16 @@ export default class AnonymizedFile {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Tree entries have no size; zero-byte files are still valid files. */
|
||||
async assertContentFile(): Promise<void> {
|
||||
const file = await this.getFileInfo();
|
||||
if (file.size == null || file.submodule) {
|
||||
throw new AnonymousError("folder_not_supported", { httpStatus: 404 });
|
||||
}
|
||||
}
|
||||
|
||||
async content(): Promise<Readable> {
|
||||
await this.assertContentFile();
|
||||
if (this.anonymizedPath.includes(config.ANONYMIZATION_MASK)) {
|
||||
await this.originalPath();
|
||||
}
|
||||
@@ -388,6 +397,7 @@ export default class AnonymizedFile {
|
||||
}
|
||||
|
||||
async anonymizedContent() {
|
||||
await this.assertContentFile();
|
||||
const anonymizer = this.repository.generateAnonymizeTransformer(
|
||||
await this.originalPath()
|
||||
);
|
||||
@@ -439,6 +449,7 @@ export default class AnonymizedFile {
|
||||
}
|
||||
|
||||
async send(res: Response): Promise<void> {
|
||||
await this.assertContentFile();
|
||||
const anonymizer = this.repository.generateAnonymizeTransformer(
|
||||
await this.originalPath()
|
||||
);
|
||||
|
||||
@@ -139,16 +139,30 @@ export default class GitHubStream extends GitHubBase {
|
||||
filePath
|
||||
);
|
||||
logger.debug("downloading via raw URL (LFS)", { url });
|
||||
const assertDownloadUrl = (value: URL) => {
|
||||
if (value.protocol !== "https:" || value.username || value.password || value.port ||
|
||||
!["raw.githubusercontent.com", "media.githubusercontent.com"].includes(value.hostname)) {
|
||||
throw new AnonymousError("upstream_error", { httpStatus: 502 });
|
||||
}
|
||||
};
|
||||
const data = this.data;
|
||||
const cancellation = new AbortController();
|
||||
let active: stream.Readable | undefined;
|
||||
const output = stream.Readable.from((async function* () {
|
||||
try {
|
||||
active = got.stream(url, {
|
||||
hooks: { beforeRequest: [async () => { await githubTokenContext(token)?.renew(); }] },
|
||||
hooks: {
|
||||
beforeRequest: [async () => { await githubTokenContext(token)?.renew(); }],
|
||||
beforeRedirect: [options => { assertDownloadUrl(options.url); }],
|
||||
},
|
||||
headers: !token || githubTokenContext(token)?.publicRepository ? {} : { authorization: `token ${token}` },
|
||||
followRedirect: true,
|
||||
});
|
||||
active.once("response", response => {
|
||||
if (response.statusCode !== 200) return;
|
||||
try { assertDownloadUrl(new URL(response.url)); }
|
||||
catch (error) { active?.destroy(error as Error); }
|
||||
});
|
||||
for await (const chunk of active) yield chunk;
|
||||
return;
|
||||
} catch (error) {
|
||||
@@ -173,12 +187,6 @@ export default class GitHubStream extends GitHubBase {
|
||||
if (cancellation.signal.aborted) return;
|
||||
const metadata = response.data as { type?: string; download_url?: string };
|
||||
if (metadata.type !== "file" || !metadata.download_url) throw error;
|
||||
const assertDownloadUrl = (value: URL) => {
|
||||
if (value.protocol !== "https:" || value.username || value.password || value.port ||
|
||||
!["raw.githubusercontent.com", "media.githubusercontent.com"].includes(value.hostname)) {
|
||||
throw new AnonymousError("upstream_error", { httpStatus: 502 });
|
||||
}
|
||||
};
|
||||
assertDownloadUrl(new URL(metadata.download_url));
|
||||
// The API URL supplies its own authorization. Never forward the
|
||||
// owner's token to the download host or a redirect destination.
|
||||
|
||||
@@ -115,6 +115,7 @@ router.get(
|
||||
anonymizedPath,
|
||||
});
|
||||
const originalPath = await f.originalPath();
|
||||
await f.assertContentFile();
|
||||
if (!f.isFileSupported()) {
|
||||
throw new AnonymousError("file_not_supported", {
|
||||
httpStatus: 403,
|
||||
|
||||
Reference in new issue
Block a user