Skip to content

Commit 5f229a0

Browse files
committed
Enforce string length (#76)
* Add utility function to enforce length * Enforce length in CRUD * Enforce length in jobs * Change log function for trim strings * Add maxlength attribute to text fields Fix loop condition
1 parent 8977896 commit 5f229a0

23 files changed

Lines changed: 454 additions & 129 deletions

File tree

src/lib/server/actions/project.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { IAmWrapper } from '../aws/iamwrapper';
33
import { prisma } from '../prisma';
44
import { Utils } from '../utils';
55
import { Project } from '$lib/server/models/project';
6+
import { trimStrings } from '$lib/valibot';
67

78
export class ProjectUpdateOperation {
89
private id;
@@ -66,11 +67,14 @@ export class ProjectUpdateOperation {
6667
const url = this.adjustUrl(project.url!, publicKeyId!);
6768
await prisma.project.update({
6869
where: { id: project.id },
69-
data: {
70-
user_id,
71-
publishing_key,
72-
url
73-
}
70+
data: trimStrings(
71+
{
72+
user_id,
73+
publishing_key,
74+
url
75+
},
76+
'project'
77+
)
7478
});
7579
}
7680
private adjustUrl(url: string, newPublicKeyId: string) {

src/lib/server/job-executors/build.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { CodeCommit } from '../aws/codecommit';
77
import { BullMQ, getQueues } from '../bullmq';
88
import { Build } from '../models/build';
99
import { prisma } from '../prisma';
10+
import { trimStrings } from '$lib/valibot';
1011

1112
export async function product(job: Job<BullMQ.Build.Product>): Promise<unknown> {
1213
try {
@@ -65,12 +66,16 @@ export async function product(job: Job<BullMQ.Build.Product>): Promise<unknown>
6566
if (lastBuildGuid) {
6667
await prisma.build.update({
6768
where: { id: build.id },
68-
data: {
69-
build_guid: lastBuildGuid,
70-
codebuild_url: CodeBuild.getCodeBuildUrl('build_app', lastBuildGuid),
71-
console_text_url: CodeBuild.getConsoleTextUrl('build_app', lastBuildGuid),
72-
status: Build.Status.Active
73-
}
69+
data: trimStrings(
70+
{
71+
build_guid: lastBuildGuid,
72+
codebuild_url: CodeBuild.getCodeBuildUrl('build_app', lastBuildGuid),
73+
console_text_url: CodeBuild.getConsoleTextUrl('build_app', lastBuildGuid),
74+
status: Build.Status.Active
75+
},
76+
'build',
77+
job.log
78+
)
7479
});
7580
}
7681
const name = `Check status of Build #${build.id}`;
@@ -110,12 +115,16 @@ export async function product(job: Job<BullMQ.Build.Product>): Promise<unknown>
110115
if (lastBuildGuid) {
111116
await prisma.build.update({
112117
where: { id: build.id },
113-
data: {
114-
build_guid: lastBuildGuid,
115-
codebuild_url: CodeBuild.getCodeBuildUrl('build_app', lastBuildGuid),
116-
console_text_url: CodeBuild.getConsoleTextUrl('build_app', lastBuildGuid),
117-
status: Build.Status.Active
118-
}
118+
data: trimStrings(
119+
{
120+
build_guid: lastBuildGuid,
121+
codebuild_url: CodeBuild.getCodeBuildUrl('build_app', lastBuildGuid),
122+
console_text_url: CodeBuild.getConsoleTextUrl('build_app', lastBuildGuid),
123+
status: Build.Status.Active
124+
},
125+
'build',
126+
job.log
127+
)
119128
});
120129
}
121130
const name = `Check status of Build #${build.id}`;
@@ -136,11 +145,15 @@ export async function product(job: Job<BullMQ.Build.Product>): Promise<unknown>
136145
job.log(`${e}`);
137146
await prisma.build.update({
138147
where: { id: job.data.buildId },
139-
data: {
140-
result: Build.Result.Failure,
141-
status: Build.Status.Completed,
142-
error: String(e)
143-
}
148+
data: trimStrings(
149+
{
150+
result: Build.Result.Failure,
151+
status: Build.Status.Completed,
152+
error: String(e)
153+
},
154+
'build',
155+
job.log
156+
)
144157
});
145158
}
146159
}

src/lib/server/job-executors/polling.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { BullMQ, getQueues } from '../bullmq';
55
import { Build } from '../models/build';
66
import { prisma } from '../prisma';
77
import { Release } from '$lib/server/models/release';
8+
import type { Logger } from '$lib/utils';
9+
import { trimStrings } from '$lib/valibot';
810

911
export async function build(job: Job<BullMQ.Polling.Build>): Promise<unknown> {
1012
try {
@@ -56,7 +58,7 @@ export async function build(job: Job<BullMQ.Polling.Build>): Promise<unknown> {
5658
}
5759
await prisma.build.update({
5860
where: { id: build.id },
59-
data: { ...build, job: undefined }
61+
data: trimStrings({ ...build, job: undefined }, 'build', job.log)
6062
});
6163
job.updateProgress(100);
6264
return {
@@ -71,11 +73,15 @@ export async function build(job: Job<BullMQ.Polling.Build>): Promise<unknown> {
7173
job.log(`${e}`);
7274
await prisma.build.update({
7375
where: { id: job.data.buildId },
74-
data: {
75-
result: Build.Result.Failure,
76-
status: Build.Status.Completed,
77-
error: String(e)
78-
}
76+
data: trimStrings(
77+
{
78+
result: Build.Result.Failure,
79+
status: Build.Status.Completed,
80+
error: String(e)
81+
},
82+
'build',
83+
job.log
84+
)
7985
});
8086
}
8187
}
@@ -123,11 +129,11 @@ export async function release(job: Job<BullMQ.Polling.Release>): Promise<unknown
123129
case CodeBuild.Status.Fault:
124130
case CodeBuild.Status.TimedOut:
125131
release.result = Build.Result.Failure;
126-
await handleReleaseFailure(release);
132+
await handleReleaseFailure(release, job.log);
127133
break;
128134
case CodeBuild.Status.Stopped:
129135
release.result = Build.Result.Aborted;
130-
await handleReleaseFailure(release);
136+
await handleReleaseFailure(release, job.log);
131137
break;
132138
case CodeBuild.Status.Succeeded:
133139
release.result = Build.Result.Success;
@@ -164,11 +170,12 @@ export async function release(job: Job<BullMQ.Polling.Release>): Promise<unknown
164170
}
165171

166172
async function handleReleaseFailure(
167-
release: Prisma.releaseGetPayload<{ select: { id: true; console_text_url: true } }>
173+
release: Prisma.releaseGetPayload<{ select: { id: true; console_text_url: true } }>,
174+
log: Logger
168175
) {
169176
await prisma.release.update({
170177
where: { id: release.id },
171-
data: { error: release.console_text_url }
178+
data: trimStrings({ error: release.console_text_url }, 'release', log)
172179
});
173180
await getQueues().S3.add(`Save Errors for Release ${release.id} to S3`, {
174181
type: BullMQ.JobType.S3_CopyError,

src/lib/server/job-executors/release.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { BullMQ, getQueues } from '../bullmq';
55
import { prisma } from '../prisma';
66
import { Build } from '$lib/server/models/build';
77
import { Release } from '$lib/server/models/release';
8+
import { trimStrings } from '$lib/valibot';
89

910
export async function product(job: Job<BullMQ.Release.Product>): Promise<unknown> {
1011
try {
@@ -32,12 +33,16 @@ export async function product(job: Job<BullMQ.Release.Product>): Promise<unknown
3233
if (lastBuildGuid) {
3334
await prisma.release.update({
3435
where: { id: job.data.releaseId },
35-
data: {
36-
build_guid: lastBuildGuid,
37-
codebuild_url: CodeBuild.getCodeBuildUrl('publish_app', lastBuildGuid),
38-
console_text_url: CodeBuild.getConsoleTextUrl('publish_app', lastBuildGuid),
39-
status: Release.Status.Active
40-
}
36+
data: trimStrings(
37+
{
38+
build_guid: lastBuildGuid,
39+
codebuild_url: CodeBuild.getCodeBuildUrl('publish_app', lastBuildGuid),
40+
console_text_url: CodeBuild.getConsoleTextUrl('publish_app', lastBuildGuid),
41+
status: Release.Status.Active
42+
},
43+
'release',
44+
job.log
45+
)
4146
});
4247
}
4348
const name = `Check status of Release #${release.id}`;
@@ -54,11 +59,15 @@ export async function product(job: Job<BullMQ.Release.Product>): Promise<unknown
5459
job.log(`${e}`);
5560
await prisma.release.update({
5661
where: { id: job.data.releaseId },
57-
data: {
58-
result: Build.Result.Failure,
59-
status: Release.Status.Completed,
60-
error: String(e)
61-
}
62+
data: trimStrings(
63+
{
64+
result: Build.Result.Failure,
65+
status: Release.Status.Completed,
66+
error: String(e)
67+
},
68+
'release',
69+
job.log
70+
)
6271
});
6372
}
6473
}

src/lib/server/job-executors/s3.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { BuildForPrefix } from '$lib/server/models/artifacts';
66
import { Build } from '$lib/server/models/build';
77
import { Release } from '$lib/server/models/release';
88
import { prisma } from '$lib/server/prisma';
9+
import { trimStrings } from '$lib/valibot';
910

1011
export async function save(job: Job<BullMQ.S3.CopyArtifacts>): Promise<unknown> {
1112
const id = job.data.id;
@@ -31,7 +32,7 @@ export async function save(job: Job<BullMQ.S3.CopyArtifacts>): Promise<unknown>
3132
if (build?.job) {
3233
await s3.copyS3Folder(build);
3334
let defaultLanguage = await s3.readS3File(build, 'play-listing/default-language.txt');
34-
console.log(`getExtraContent defaultLanguage: ${defaultLanguage}`);
35+
job.log(`getExtraContent defaultLanguage: ${defaultLanguage}`);
3536
const manifestFileContent = await s3.readS3File(build, 'manifest.txt');
3637
let manifest: Record<string, string | string[] | Record<string, string>> = {};
3738
if (manifestFileContent) {
@@ -104,12 +105,16 @@ export async function save(job: Job<BullMQ.S3.CopyArtifacts>): Promise<unknown>
104105
}
105106
await prisma.build.update({
106107
where: { id },
107-
data: {
108-
...build,
109-
status: Build.Status.Completed,
110-
result: Build.Result.Success,
111-
job: undefined
112-
}
108+
data: trimStrings(
109+
{
110+
...build,
111+
status: Build.Status.Completed,
112+
result: Build.Result.Success,
113+
job: undefined
114+
},
115+
'build',
116+
job.log
117+
)
113118
});
114119
await s3.removeCodeBuildFolder(build);
115120
job.updateProgress(100);

src/lib/utils/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
export function ErrorResponse(status: number, message: string, headers?: HeadersInit) {
22
return new Response(JSON.stringify({ status, message }), { status, headers });
33
}
4+
5+
export type Logger = (msg: string) => void;
6+
export const defaultLogger: Logger = (msg) => console.log(msg);

src/lib/valibot.ts

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as v from 'valibot';
2+
import { type Logger, defaultLogger } from './utils';
23

34
export const idSchema = v.pipe(v.number(), v.minValue(0), v.integer());
45

@@ -7,14 +8,14 @@ export const paramNumber = v.pipe(
78
v.transform((s) => parseInt(s))
89
);
910

10-
export function convertEmptyStrToNull() {
11+
export function convertEmptyStrToNull(limit?: number) {
1112
return v.nullable(
1213
v.union([
1314
v.pipe(
1415
v.literal(''),
1516
v.transform(() => null)
1617
),
17-
v.string()
18+
limit ? v.pipe(v.string(), v.maxBytes(limit)) : v.string()
1819
])
1920
);
2021
}
@@ -42,3 +43,79 @@ export const tableSchema = v.object({
4243
})
4344
)
4445
});
46+
47+
export const stringLimits = {
48+
build: {
49+
status: 255,
50+
result: 255,
51+
error: 2083,
52+
channel: 255,
53+
artifact_url_base: 2083,
54+
artifact_files: 4096,
55+
build_guid: 255,
56+
console_text_url: 255,
57+
codebuild_url: 255,
58+
targets: 255
59+
},
60+
client: {
61+
access_token: 255,
62+
prefix: 4
63+
},
64+
job: {
65+
request_id: 255,
66+
git_url: 2083,
67+
app_id: 255,
68+
publisher_id: 255,
69+
jenkins_build_url: 1024,
70+
jenkins_publish_url: 1024
71+
},
72+
project: {
73+
status: 255,
74+
result: 255,
75+
error: 2083,
76+
url: 1024,
77+
user_id: 255,
78+
group_id: 255,
79+
app_id: 255,
80+
project_name: 255,
81+
language_code: 255,
82+
publishing_key: 1024
83+
},
84+
release: {
85+
status: 255,
86+
result: 255,
87+
error: 2083,
88+
channel: 255,
89+
title: 30,
90+
defaultLanguage: 255,
91+
promote_from: 255,
92+
build_guid: 255,
93+
console_text_url: 255,
94+
codebuild_url: 255,
95+
targets: 255,
96+
artifact_url_base: 255,
97+
artifact_files: 255
98+
}
99+
} as const;
100+
101+
export function trimStrings<T extends Record<string, unknown>>(
102+
obj: T,
103+
scope: keyof typeof stringLimits,
104+
log: Logger = defaultLogger
105+
) {
106+
for (const [key, limit] of Object.entries(stringLimits[scope])) {
107+
const raw = obj[key];
108+
if (raw) {
109+
let val = (raw as string).trim().substring(0, limit);
110+
while (new Blob([val]).size > limit) {
111+
val = val.substring(0, val.length - 1);
112+
}
113+
if (raw !== val) {
114+
log(`trimStrings ${scope}: "${raw}" => "${val}"`);
115+
//@ts-expect-error this should be fine...
116+
obj[key] = val;
117+
}
118+
}
119+
}
120+
return obj;
121+
}

src/routes/(api)/job/+server.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import * as v from 'valibot';
22
import type { RequestHandler } from './$types';
33
import { prisma } from '$lib/server/prisma';
44
import { ErrorResponse } from '$lib/utils';
5-
import { stringIdSchema } from '$lib/valibot';
5+
import { stringIdSchema, stringLimits } from '$lib/valibot';
66

77
const jobSchema = v.strictObject({
88
request_id: stringIdSchema,
9-
git_url: v.pipe(v.string(), v.url()),
10-
app_id: v.string(),
11-
publisher_id: v.string()
9+
git_url: v.pipe(v.string(), v.url(), v.maxBytes(stringLimits.job.git_url)),
10+
app_id: v.pipe(v.string(), v.maxBytes(stringLimits.job.app_id)),
11+
publisher_id: v.pipe(v.string(), v.maxBytes(stringLimits.job.publisher_id))
1212
});
1313

1414
// POST /job

0 commit comments

Comments
 (0)