Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export namespace CreateConfigProfileCommand {
/^[A-Za-z0-9_\s-]+$/,
'Name can only contain letters, numbers, underscores, dashes and spaces',
),
config: z.object({}).passthrough(),
config: z.union([z.string(), z.object({}).passthrough()]),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Service signature not updated to accept string

The Zod schema now infers config as string | Record<string, unknown>, but ConfigProfileService.createConfigProfile still declares config: object:

public async createConfigProfile(
    name: string,
    config: object,  // ← only object, not string | object
)

In TypeScript, string is not assignable to object, so passing createConfigProfileDto.config (which is string | Record<string, unknown>) to this method is a type error that will break compilation.

The service (and likewise updateConfigProfile) needs its signature updated:

public async createConfigProfile(
    name: string,
    config: string | object,
)

The same applies to src/modules/config-profiles/config-profile.service.ts line 133 (createConfigProfile) and line 192 (updateConfigProfile).

});

export type Request = z.infer<typeof RequestSchema>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export namespace UpdateConfigProfileCommand {
'Name can only contain letters, numbers, underscores, dashes and spaces',
)
.optional(),
config: z.object({}).passthrough().optional(),
config: z.union([z.string(), z.object({}).passthrough()]).optional(),
});

export type Request = z.infer<typeof RequestSchema>;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"husky": "9.1.7",
"hygen": "^6.2.11",
"ioredis": "^5.9.3",
"jsonc-parser": "^3.3.1",
"jsonwebtoken": "^9.0.3",
"kysely": "^0.28.11",
"morgan": "^1.10.1",
Expand Down
5 changes: 3 additions & 2 deletions src/common/helpers/xray-config/xray-config.validator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createPublicKey, createPrivateKey, KeyObject } from 'node:crypto';
import { hasher } from 'node-object-hash';
import { readFileSync } from 'node:fs';
import { parse } from 'jsonc-parser';

import { HashedSet } from '@remnawave/hashed-set';

Expand Down Expand Up @@ -61,9 +62,9 @@ export class XRayConfig {

if (typeof configInput === 'string') {
try {
config = JSON.parse(configInput) as IXrayConfig;
config = parse(configInput) as IXrayConfig;
} catch (error) {
throw new Error(`Invalid JSON input or file path: ${error}`);
throw new Error(`Invalid JSON/JSONC input: ${error}`);
}
Comment on lines 64 to 68
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 jsonc-parser's parse() never throws — try-catch is dead code

According to the official jsonc-parser docs: "On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. Therefore always check the errors list to find out if the input was valid."

parse() does not throw on invalid JSON/JSONC. It returns undefined (or a partial result) for completely invalid input and communicates errors through the optional second errors array argument. This means:

  1. The catch block is unreachable dead code for any malformed JSONC string
  2. If the input is completely invalid, parse() returns undefined, so config ends up as undefined, and the subsequent this.config.inbounds access in validate() causes a cryptic TypeError instead of the intended clear "Invalid JSON/JSONC input" message

The fix is to pass an errors array and check it explicitly:

import { parse, ParseError } from 'jsonc-parser';

if (typeof configInput === 'string') {
    const errors: ParseError[] = [];
    config = parse(configInput, errors) as IXrayConfig;
    if (errors.length > 0) {
        throw new Error(`Invalid JSON/JSONC input: error code ${errors[0].error} at offset ${errors[0].offset}`);
    }
}

} else if (typeof configInput === 'object') {
config = configInput as IXrayConfig;
Expand Down