mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 15:20:13 +00:00
fix(core): address PR feedback — ReDoS-safe slug trim, getVariables cleanups
- slugify: replace the anchored alternated trim regex (/^-+|-+$/g) with a character-scan trim — CodeQL js/polynomial-redos blocker. - readRenderOverrides: fold the readOverrides wrapper into the exported function (one name, no pass-through). - getVariables: deduplicate declarers with a Set, matching injectCompositionCssVariables. - Move the tokenSlug import to the top of the file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e2c88ef689
commit
d9368ec051
@@ -19,6 +19,8 @@
|
|||||||
*
|
*
|
||||||
* const { title = "Untitled", theme = "light" } = getVariables<MyVars>();
|
* const { title = "Untitled", theme = "light" } = getVariables<MyVars>();
|
||||||
*/
|
*/
|
||||||
|
import { cssVariableName, detectSlugCollisions } from "../tokenSlug";
|
||||||
|
|
||||||
export function getVariables<
|
export function getVariables<
|
||||||
T extends Record<string, unknown> = Record<string, unknown>,
|
T extends Record<string, unknown> = Record<string, unknown>,
|
||||||
>(): Partial<T> {
|
>(): Partial<T> {
|
||||||
@@ -27,11 +29,18 @@ export function getVariables<
|
|||||||
// Same collection the CSS-variable injection uses: <html> first, then any
|
// Same collection the CSS-variable injection uses: <html> first, then any
|
||||||
// composition element carrying the attribute (later declarers win), then
|
// composition element carrying the attribute (later declarers win), then
|
||||||
// render-time overrides.
|
// render-time overrides.
|
||||||
const declaredDefaults = readDeclaredDefaults(document.documentElement);
|
const declarers = new Set<Element>();
|
||||||
|
if (document.documentElement?.hasAttribute("data-composition-variables")) {
|
||||||
|
declarers.add(document.documentElement);
|
||||||
|
}
|
||||||
for (const el of Array.from(document.querySelectorAll("[data-composition-variables]"))) {
|
for (const el of Array.from(document.querySelectorAll("[data-composition-variables]"))) {
|
||||||
|
declarers.add(el);
|
||||||
|
}
|
||||||
|
const declaredDefaults: Record<string, unknown> = {};
|
||||||
|
for (const el of declarers) {
|
||||||
Object.assign(declaredDefaults, readDeclaredDefaults(el));
|
Object.assign(declaredDefaults, readDeclaredDefaults(el));
|
||||||
}
|
}
|
||||||
const overrides = readOverrides();
|
const overrides = readRenderOverrides();
|
||||||
|
|
||||||
return { ...declaredDefaults, ...overrides } as Partial<T>;
|
return { ...declaredDefaults, ...overrides } as Partial<T>;
|
||||||
}
|
}
|
||||||
@@ -65,8 +74,6 @@ export function readDeclaredDefaults(root: Element | null): Record<string, unkno
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
import { cssVariableName, detectSlugCollisions } from "../tokenSlug";
|
|
||||||
|
|
||||||
const APPLIED_VARS_ATTR = "data-hf-css-vars";
|
const APPLIED_VARS_ATTR = "data-hf-css-vars";
|
||||||
|
|
||||||
function hasInlineStyle(target: Element): target is Element & ElementCSSInlineStyle {
|
function hasInlineStyle(target: Element): target is Element & ElementCSSInlineStyle {
|
||||||
@@ -125,7 +132,7 @@ export function injectCompositionCssVariables(doc: Document): void {
|
|||||||
for (const el of Array.from(doc.querySelectorAll("[data-composition-variables]"))) {
|
for (const el of Array.from(doc.querySelectorAll("[data-composition-variables]"))) {
|
||||||
declarers.add(el);
|
declarers.add(el);
|
||||||
}
|
}
|
||||||
const overrides = readOverrides();
|
const overrides = readRenderOverrides();
|
||||||
const allIds: string[] = [];
|
const allIds: string[] = [];
|
||||||
for (const el of declarers) {
|
for (const el of declarers) {
|
||||||
allIds.push(...applyDeclaredForElement(el, overrides, doc.defaultView));
|
allIds.push(...applyDeclaredForElement(el, overrides, doc.defaultView));
|
||||||
@@ -181,10 +188,6 @@ export function parseHostVariableValues(host: Element): Record<string, unknown>
|
|||||||
|
|
||||||
/** Render-time variable overrides (`hyperframes render --variables`). */
|
/** Render-time variable overrides (`hyperframes render --variables`). */
|
||||||
export function readRenderOverrides(): Record<string, unknown> {
|
export function readRenderOverrides(): Record<string, unknown> {
|
||||||
return readOverrides();
|
|
||||||
}
|
|
||||||
|
|
||||||
function readOverrides(): Record<string, unknown> {
|
|
||||||
if (typeof window === "undefined") return {};
|
if (typeof window === "undefined") return {};
|
||||||
const raw = (window as Window & { __hfVariables?: unknown }).__hfVariables;
|
const raw = (window as Window & { __hfVariables?: unknown }).__hfVariables;
|
||||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
||||||
|
|||||||
@@ -8,10 +8,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function slugify(name: string): string {
|
export function slugify(name: string): string {
|
||||||
const slug = name
|
const collapsed = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
||||||
.toLowerCase()
|
// Character-scan trim of leading/trailing "-" instead of /^-+|-+$/:
|
||||||
.replace(/[^a-z0-9]+/g, "-")
|
// CodeQL flags the alternated anchored regex as polynomial ReDoS on
|
||||||
.replace(/^-+|-+$/g, "");
|
// adversarial inputs (js/polynomial-redos).
|
||||||
|
let start = 0;
|
||||||
|
let end = collapsed.length;
|
||||||
|
while (start < end && collapsed[start] === "-") start++;
|
||||||
|
while (end > start && collapsed[end - 1] === "-") end--;
|
||||||
|
const slug = collapsed.slice(start, end);
|
||||||
return slug.length > 0 ? slug : "node";
|
return slug.length > 0 ? slug : "node";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user