Skip to content

Commit 88f40c1

Browse files
authored
chore: make repositories validate arguments before sending them to APIs (#59)
1 parent 55612cc commit 88f40c1

File tree

3 files changed

+29
-6
lines changed

3 files changed

+29
-6
lines changed

mclib/src/repos/CurseForgeRepository.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { IRepository } from "./IRepository";
22
import { RawModRepoRelease, ModRepositoryName, ModLoader, ModRepoMetadata, MCVersion } from "..";
33
import { cf_fingerprint } from 'cf-fingerprint';
44
import { logger } from "../logger";
5+
import { validateParam } from "../utils";
56

67
// Translation map for Curseforge modloader IDs
78
// Source: https://docs.curseforge.com/rest-api/#tocS_ModLoaderType
@@ -25,6 +26,8 @@ export class CurseForgeRepository implements IRepository {
2526
}
2627

2728
async getModReleases(modId: string): Promise<RawModRepoRelease[]> {
29+
validateParam(modId);
30+
2831
const modInfo = await this.fetchModInfo(Number(modId));
2932
if (!modInfo || !modInfo.latestFilesIndexes) return [];
3033

@@ -47,6 +50,9 @@ export class CurseForgeRepository implements IRepository {
4750
}
4851

4952
async searchMods(query: string, maxResults: number): Promise<ModRepoMetadata[]> {
53+
validateParam(query);
54+
validateParam(maxResults.toString());
55+
5056
type Data = {
5157
data: {
5258
id: number;
@@ -70,7 +76,7 @@ export class CurseForgeRepository implements IRepository {
7076
pageSize: maxResults.toString(),
7177
sortField: "1",
7278
sortOrder: "desc",
73-
searchFilter: query
79+
searchFilter: query,
7480
}));
7581
if (!resp.ok) throw new Error("Failed to fetch search results from CurseForge");
7682
const jsonResp: Data = (await resp.json());
@@ -87,12 +93,16 @@ export class CurseForgeRepository implements IRepository {
8793
}
8894

8995
private async fetchModInfo(modId: number): Promise<ModInfoData | null> {
96+
validateParam(modId.toString());
97+
9098
const modResp = await this.fetchClient(`${CurseForgeRepository.BASE_URL}/mods/${modId}`);
9199
if (!modResp.ok) return null;
92100
return (await modResp.json()).data as ModInfoData;
93101
}
94102

95103
async getByDataHash(hash: string): Promise<ModRepoMetadata | null> {
104+
validateParam(hash);
105+
96106
// Use the CurseForge API to get file info by fingerprint
97107
const resp = await this.fetchClient(`${CurseForgeRepository.BASE_URL}/fingerprints`, {
98108
method: 'POST',

mclib/src/repos/ModrinthRepository.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import type { IRepository } from "./IRepository";
22
import { ModRepositoryName, ModRepoMetadata, ModLoaderUtil, RawModRepoRelease } from "..";
3+
import { validateParam } from "../utils";
34

45
// https://devblogs.microsoft.com/typescript/announcing-typescript-5-9-rc/#notable-behavioral-changes
56
function toArrayBuffer(buf: ArrayBufferLike): ArrayBuffer {
6-
if (buf instanceof ArrayBuffer) return buf;
7-
// SharedArrayBuffer → copy into a new ArrayBuffer
8-
const copy = new Uint8Array(buf.byteLength);
9-
copy.set(new Uint8Array(buf));
10-
return copy.buffer;
7+
if (buf instanceof ArrayBuffer) return buf;
8+
// SharedArrayBuffer → copy into a new ArrayBuffer
9+
const copy = new Uint8Array(buf.byteLength);
10+
copy.set(new Uint8Array(buf));
11+
return copy.buffer;
1112
}
1213

1314

@@ -20,6 +21,8 @@ export class ModrinthRepository implements IRepository {
2021
}
2122

2223
async getModReleases(modId: string): Promise<RawModRepoRelease[]> {
24+
validateParam(modId);
25+
2326
type Data = Array<{
2427
game_versions: string[];
2528
version_number: string;
@@ -49,6 +52,9 @@ export class ModrinthRepository implements IRepository {
4952
}
5053

5154
async searchMods(query: string, maxResults: number): Promise<ModRepoMetadata[]> {
55+
validateParam(query);
56+
validateParam(maxResults.toString());
57+
5258
type Data = {
5359
hits: Array<{
5460
slug: string;
@@ -74,6 +80,8 @@ export class ModrinthRepository implements IRepository {
7480
}
7581

7682
async getByDataHash(hash: string): Promise<ModRepoMetadata | null> {
83+
validateParam(hash);
84+
7785
// Get version info using the hash
7886
const versionResp = await this.fetchClient(`https://api.modrinth.com/v2/version_file/${hash}`);
7987
if (!versionResp.ok) return null;

mclib/src/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function validateParam(s: string) {
2+
if (!/^[a-zA-Z0-9]{0,64}$/.test(s)) {
3+
throw new Error('Invalid parameter: must be 0-64 alphanumeric characters');
4+
}
5+
}

0 commit comments

Comments
 (0)