Skip to content

Commit 7c75c40

Browse files
committed
feat: enhance error handling and input validation in VM functions
1 parent c8ecd5b commit 7c75c40

4 files changed

Lines changed: 164 additions & 26 deletions

File tree

frontend.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
name: frontend-design
3+
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
4+
license: Complete terms in LICENSE.txt
5+
---
6+
7+
This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
8+
9+
The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
10+
11+
## Design Thinking
12+
13+
Before coding, understand the context and commit to a BOLD aesthetic direction:
14+
15+
- **Purpose**: What problem does this interface solve? Who uses it?
16+
- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
17+
- **Constraints**: Technical requirements (framework, performance, accessibility).
18+
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
19+
20+
**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.
21+
22+
Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
23+
24+
- Production-grade and functional
25+
- Visually striking and memorable
26+
- Cohesive with a clear aesthetic point-of-view
27+
- Meticulously refined in every detail
28+
29+
## Frontend Aesthetics Guidelines
30+
31+
Focus on:
32+
33+
- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
34+
- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
35+
- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
36+
- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
37+
- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.
38+
39+
NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.
40+
41+
Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.
42+
43+
**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.
44+
45+
Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.

wasm/src/vm_bridge.c

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <emscripten.h>
2+
#include <limits.h>
23
#include <math.h>
34
#include <stdint.h>
45
#include <stdio.h>
@@ -59,6 +60,26 @@ static void set_last_js_error(const char *prefix) {
5960
JS_FreeValue(g_ctx, exc);
6061
}
6162

63+
static void parse_box_values(const char *arg, float *bx, float *by, float *bw,
64+
float *bh) {
65+
float parsed_x;
66+
float parsed_y;
67+
float parsed_w;
68+
float parsed_h;
69+
70+
if (!arg)
71+
return;
72+
73+
if (sscanf(arg, "%f,%f,%f,%f", &parsed_x, &parsed_y, &parsed_w,
74+
&parsed_h) != 4)
75+
return;
76+
77+
*bx = parsed_x;
78+
*by = parsed_y;
79+
*bw = parsed_w;
80+
*bh = parsed_h;
81+
}
82+
6283
static JSValue js_vm_ts(JSContext *ctx, JSValueConst this_val, int argc,
6384
JSValueConst *argv) {
6485
double t = EM_ASM_DOUBLE({ return Date.now(); });
@@ -97,6 +118,8 @@ static char *read_browser_global(const char *name) {
97118
var str = JSON.stringify(obj);
98119
var len = lengthBytesUTF8(str) + 1;
99120
var ptr = _malloc(len);
121+
if (!ptr)
122+
return 0;
100123
stringToUTF8(str, ptr, len);
101124
return ptr;
102125
},
@@ -133,6 +156,8 @@ static char *call_browser_fn(const char *fn_name, const char *arg) {
133156
return 0;
134157
var len = lengthBytesUTF8(result) + 1;
135158
var ptr = _malloc(len);
159+
if (!ptr)
160+
return 0;
136161
stringToUTF8(result, ptr, len);
137162
return ptr;
138163
},
@@ -192,6 +217,13 @@ static void clamp_box(float *bx, float *by, float *bw, float *bh) {
192217

193218
static int capture_video_frame(uint8_t **pixels, int *video_width,
194219
int *video_height) {
220+
if (!pixels || !video_width || !video_height)
221+
return 0;
222+
223+
*pixels = NULL;
224+
*video_width = 0;
225+
*video_height = 0;
226+
195227
int vw = EM_ASM_INT({
196228
var v = document.querySelector('video');
197229
return v ? v.videoWidth : 0;
@@ -203,24 +235,35 @@ static int capture_video_frame(uint8_t **pixels, int *video_width,
203235

204236
if (vw <= 0 || vh <= 0)
205237
return 0;
238+
if ((size_t)vw > ((size_t)INT_MAX / 4) / (size_t)vh)
239+
return 0;
206240

207-
int size = vw * vh * 4;
208-
uint8_t *buffer = (uint8_t *)malloc(size);
241+
size_t frame_size = (size_t)vw * (size_t)vh * 4u;
242+
uint8_t *buffer = (uint8_t *)malloc(frame_size);
209243
if (!buffer)
210244
return 0;
211245

212-
EM_ASM(
246+
int copied = EM_ASM_INT(
213247
{
214248
var v = document.querySelector('video');
249+
if (!v)
250+
return 0;
215251
var c = document.createElement('canvas');
216252
c.width = $1;
217253
c.height = $2;
218254
var g = c.getContext('2d');
255+
if (!g)
256+
return 0;
219257
g.drawImage(v, 0, 0, $1, $2);
220258
var d = g.getImageData(0, 0, $1, $2);
221259
HEAPU8.set(d.data, $0);
260+
return 1;
222261
},
223262
buffer, vw, vh);
263+
if (!copied) {
264+
free(buffer);
265+
return 0;
266+
}
224267

225268
*pixels = buffer;
226269
*video_width = vw;
@@ -311,7 +354,7 @@ static JSValue js_vm_check_frame_quality(JSContext *ctx, JSValueConst this_val,
311354
if (argc > 0) {
312355
const char *arg = JS_ToCString(ctx, argv[0]);
313356
if (arg) {
314-
sscanf(arg, "%f,%f,%f,%f", &bx, &by, &bw, &bh);
357+
parse_box_values(arg, &bx, &by, &bw, &bh);
315358
JS_FreeCString(ctx, arg);
316359
}
317360
}
@@ -359,7 +402,7 @@ static JSValue js_vm_infer_age(JSContext *ctx, JSValueConst this_val, int argc,
359402
if (argc > 0) {
360403
const char *arg = JS_ToCString(ctx, argv[0]);
361404
if (arg) {
362-
sscanf(arg, "%f,%f,%f,%f", &bx, &by, &bw, &bh);
405+
parse_box_values(arg, &bx, &by, &bw, &bh);
363406
JS_FreeCString(ctx, arg);
364407
}
365408
}
@@ -468,30 +511,39 @@ static void write_u32_le(uint8_t *out, uint32_t v) {
468511

469512
EMSCRIPTEN_KEEPALIVE
470513
uint8_t *vm_decrypt_blob(const uint8_t *input, int input_len, int *out_len) {
514+
if (!input || !out_len)
515+
return NULL;
516+
471517
*out_len = 0;
472518

473519
if (input_len < 20)
474520
return NULL;
475521

476522
uint32_t plain_len = read_u32_le(input);
477523
uint32_t ct_len = read_u32_le(input + 4);
524+
size_t header_len = 8u + NONCE_LEN;
478525

479-
if (input_len < (int)(8 + NONCE_LEN + ct_len))
526+
if (plain_len != ct_len)
527+
return NULL;
528+
if ((size_t)ct_len > (size_t)input_len - header_len)
529+
return NULL;
530+
if (plain_len > (uint32_t)INT_MAX)
480531
return NULL;
481532

482533
const uint8_t *nonce = input + 8;
483534
const uint8_t *ct = input + 8 + NONCE_LEN;
484535

485-
uint8_t *out = (uint8_t *)malloc(plain_len);
536+
size_t output_len = (size_t)plain_len;
537+
uint8_t *out = (uint8_t *)malloc(output_len == 0 ? 1 : output_len);
486538
if (!out)
487539
return NULL;
488540

489541
uint8_t dk[32];
490542
VM_DERIVE_KEY(VM_KEY_ID_DECRYPT, dk);
491-
chacha20(out, ct, plain_len, dk, nonce, 1);
543+
chacha20(out, ct, output_len, dk, nonce, 1);
492544
wipe((volatile uint8_t *)dk, 32);
493545

494-
*out_len = (int)plain_len;
546+
*out_len = (int)output_len;
495547
return out;
496548
}
497549

@@ -500,23 +552,39 @@ const char *vm_last_error(void) { return g_last_error; }
500552

501553
static uint8_t *encrypt_and_sign(const char *str, size_t str_len,
502554
int *out_len) {
555+
if (!str || !out_len)
556+
return NULL;
557+
if (str_len > SIZE_MAX - (8u + NONCE_LEN + MAC_LEN))
558+
return NULL;
559+
560+
*out_len = 0;
503561
uint8_t resp_nonce[NONCE_LEN];
504562
double ts = EM_ASM_DOUBLE({ return Date.now(); });
505563
uint64_t ts_u = (uint64_t)ts;
506564
memcpy(resp_nonce, &ts_u, 8);
507565
uint32_t ctr = antidbg_state();
508566
memcpy(resp_nonce + 8, &ctr, 4);
509567

510-
uint8_t *resp_ct = (uint8_t *)malloc(str_len);
511-
if (!resp_ct)
512-
return NULL;
568+
uint8_t *resp_ct = NULL;
569+
if (str_len > 0) {
570+
resp_ct = (uint8_t *)malloc(str_len);
571+
if (!resp_ct)
572+
return NULL;
573+
}
513574

514-
uint8_t ek[32];
515-
VM_DERIVE_KEY(VM_KEY_ID_ENCRYPT, ek);
516-
chacha20(resp_ct, (const uint8_t *)str, str_len, ek, resp_nonce, 1);
517-
wipe((volatile uint8_t *)ek, 32);
575+
if (str_len > 0) {
576+
uint8_t ek[32];
577+
VM_DERIVE_KEY(VM_KEY_ID_ENCRYPT, ek);
578+
chacha20(resp_ct, (const uint8_t *)str, str_len, ek, resp_nonce, 1);
579+
wipe((volatile uint8_t *)ek, 32);
580+
}
581+
582+
size_t total = 8u + NONCE_LEN + str_len + MAC_LEN;
583+
if (total > (size_t)INT_MAX) {
584+
free(resp_ct);
585+
return NULL;
586+
}
518587

519-
size_t total = 8 + NONCE_LEN + str_len + MAC_LEN;
520588
uint8_t *resp = (uint8_t *)malloc(total);
521589
if (!resp) {
522590
free(resp_ct);
@@ -526,23 +594,39 @@ static uint8_t *encrypt_and_sign(const char *str, size_t str_len,
526594
write_u32_le(resp, MAGIC_RESP);
527595
write_u32_le(resp + 4, (uint32_t)total);
528596
memcpy(resp + 8, resp_nonce, NONCE_LEN);
529-
memcpy(resp + 8 + NONCE_LEN, resp_ct, str_len);
597+
if (str_len > 0)
598+
memcpy(resp + 8 + NONCE_LEN, resp_ct, str_len);
530599
free(resp_ct);
531600

532601
uint8_t sk[32];
533602
VM_DERIVE_KEY(VM_KEY_ID_SIGN, sk);
534-
hmac_sha256(resp + 8 + NONCE_LEN + str_len, sk, 32, resp + 8,
535-
NONCE_LEN + str_len);
603+
int sign_result = hmac_sha256(resp + 8 + NONCE_LEN + str_len, sk, 32,
604+
resp + 8, NONCE_LEN + str_len);
536605
wipe((volatile uint8_t *)sk, 32);
606+
if (sign_result != 0) {
607+
free(resp);
608+
return NULL;
609+
}
537610

538611
*out_len = (int)total;
539612
return resp;
540613
}
541614

542615
EMSCRIPTEN_KEEPALIVE
543616
uint8_t *vm_exec_bytecode(const uint8_t *bundle, int bundle_len, int *out_len) {
617+
if (!out_len)
618+
return NULL;
619+
544620
*out_len = 0;
545621
set_last_error(NULL);
622+
if (!bundle) {
623+
set_last_error("bundle missing");
624+
return NULL;
625+
}
626+
if (!g_rt || !g_ctx) {
627+
set_last_error("vm not initialized");
628+
return NULL;
629+
}
546630

547631
antidbg_on_exec();
548632
if (antidbg_check() != 0) {
@@ -562,23 +646,23 @@ uint8_t *vm_exec_bytecode(const uint8_t *bundle, int bundle_len, int *out_len) {
562646
}
563647

564648
uint32_t bc_len = read_u32_le(bundle + 4);
565-
if ((int)(8 + NONCE_LEN + bc_len) > bundle_len) {
649+
if ((size_t)bc_len > (size_t)bundle_len - (8u + NONCE_LEN)) {
566650
set_last_error("bundle length mismatch");
567651
return NULL;
568652
}
569653

570654
const uint8_t *nonce_in = bundle + 8;
571655
const uint8_t *ct_in = bundle + 8 + NONCE_LEN;
572656

573-
uint8_t *bc = (uint8_t *)malloc(bc_len);
657+
uint8_t *bc = (uint8_t *)malloc(bc_len == 0 ? 1 : (size_t)bc_len);
574658
if (!bc) {
575659
set_last_error("bytecode allocation failed");
576660
return NULL;
577661
}
578662

579663
uint8_t dk[32];
580664
VM_DERIVE_KEY(VM_KEY_ID_DECRYPT, dk);
581-
chacha20(bc, ct_in, bc_len, dk, nonce_in, 1);
665+
chacha20(bc, ct_in, (size_t)bc_len, dk, nonce_in, 1);
582666
wipe((volatile uint8_t *)dk, 32);
583667

584668
JSValue val = JS_ReadObject(g_ctx, bc, bc_len, JS_READ_OBJ_BYTECODE);

wasm/src/vm_crypto.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ void chacha20(uint8_t *out, const uint8_t *in, size_t len,
99

1010
void sha256(uint8_t out[32], const uint8_t *data, size_t len);
1111

12-
void hmac_sha256(uint8_t out[32], const uint8_t *key, size_t key_len,
13-
const uint8_t *msg, size_t msg_len);
12+
int hmac_sha256(uint8_t out[32], const uint8_t *key, size_t key_len,
13+
const uint8_t *msg, size_t msg_len);
1414

1515
int ct_compare(const uint8_t *a, const uint8_t *b, size_t len);
1616

wasm/src/vm_inference.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#define IN AGE_INPUT
77
#define MB AGE_MAXBUF
8+
#define AGE_MODEL_FLOAT_COUNT 421731
89

910
static void conv2d_3x3(float *out, const float *in, const float *filt,
1011
const float *bias, int h, int w, int ci, int co,
@@ -256,6 +257,9 @@ static void preprocess(float *out, const uint8_t *rgba, int src_w, int src_h,
256257
} while (0)
257258

258259
int age_model_load(AgeModel *m, const float *data, int count) {
260+
if (!m || !data || count < AGE_MODEL_FLOAT_COUNT)
261+
return -1;
262+
259263
memset(m, 0, sizeof(*m));
260264
const float *cursor = data;
261265

@@ -335,6 +339,9 @@ int age_model_load(AgeModel *m, const float *data, int count) {
335339
}
336340

337341
void age_model_free(AgeModel *m) {
342+
if (!m)
343+
return;
344+
338345
for (int i = 0; i < 4; i++) {
339346
free(m->buf[i]);
340347
m->buf[i] = NULL;
@@ -350,7 +357,9 @@ void age_model_free(AgeModel *m) {
350357

351358
float age_model_infer(AgeModel *m, const uint8_t *rgba, int width, int height,
352359
float box_x, float box_y, float box_w, float box_h) {
353-
if (!m->loaded)
360+
if (!m || !rgba || width <= 0 || height <= 0 || !m->loaded)
361+
return -1.0f;
362+
if (!m->buf[0] || !m->buf[1] || !m->buf[2] || !m->buf[3])
354363
return -1.0f;
355364

356365
float *input = (float *)malloc(IN * IN * 3 * sizeof(float));

0 commit comments

Comments
 (0)