mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-14 23:07:29 +00:00
Block prototype keys in config paths
This commit is contained in:
parent
8f4691584c
commit
cdba848866
2 changed files with 25 additions and 5 deletions
10
packages/cli/src/config.test.ts
Normal file
10
packages/cli/src/config.test.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getConfigValue, setConfigValue } from "./config.js";
|
||||||
|
|
||||||
|
describe("CLI config", () => {
|
||||||
|
it("rejects prototype-polluting path segments", () => {
|
||||||
|
expect(() => setConfigValue("__proto__.polluted", "true", {})).toThrow(/prototype keys/);
|
||||||
|
expect(() => getConfigValue("constructor.prototype.polluted", {})).toThrow(/prototype keys/);
|
||||||
|
expect(({} as Record<string, unknown>).polluted).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -40,14 +40,12 @@ export function writeConfig(config: JsonObject) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConfigValue(path: string, config = readConfig()) {
|
export function getConfigValue(path: string, config = readConfig()) {
|
||||||
return path.split(".").reduce<unknown>((current, key) => (isObject(current) ? current[key] : undefined), config);
|
const parts = splitConfigPath(path);
|
||||||
|
return parts.reduce<unknown>((current, key) => (isObject(current) ? current[key] : undefined), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setConfigValue(path: string, rawValue: string, config = readConfig()) {
|
export function setConfigValue(path: string, rawValue: string, config = readConfig()) {
|
||||||
const parts = path.split(".").filter(Boolean);
|
const parts = splitConfigPath(path);
|
||||||
if (parts.length === 0) {
|
|
||||||
throw new Error("Config path cannot be empty.");
|
|
||||||
}
|
|
||||||
let current: JsonObject = config;
|
let current: JsonObject = config;
|
||||||
for (const part of parts.slice(0, -1)) {
|
for (const part of parts.slice(0, -1)) {
|
||||||
if (!isObject(current[part])) {
|
if (!isObject(current[part])) {
|
||||||
|
|
@ -59,6 +57,18 @@ export function setConfigValue(path: string, rawValue: string, config = readConf
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function splitConfigPath(path: string) {
|
||||||
|
const parts = path.split(".");
|
||||||
|
const blocked = new Set(["__proto__", "constructor", "prototype"]);
|
||||||
|
if (parts.length === 0 || parts.some((part) => part.length === 0)) {
|
||||||
|
throw new Error("Config path cannot contain empty segments.");
|
||||||
|
}
|
||||||
|
if (parts.some((part) => blocked.has(part))) {
|
||||||
|
throw new Error("Config path cannot contain prototype keys.");
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
export function parseConfigValue(value: string): unknown {
|
export function parseConfigValue(value: string): unknown {
|
||||||
if (value === "true") return true;
|
if (value === "true") return true;
|
||||||
if (value === "false") return false;
|
if (value === "false") return false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue