Skip to content
Open
Changes from 19 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e74c2f3
Add files via upload
sebastian-zak Apr 21, 2025
2b54dcf
update plugin and added unit test
MilenaChowaniec Apr 25, 2025
43809a1
deleted dist file
MilenaChowaniec Apr 29, 2025
65a6d0b
updated readme file
MilenaChowaniec Apr 29, 2025
d4b161a
updated files
MilenaChowaniec May 8, 2025
08b98c5
update readme
MilenaChowaniec May 8, 2025
52d3671
changed Makefile name
MilenaChowaniec May 9, 2025
6356db6
Added yml files, updated plugin
MilenaChowaniec May 15, 2025
22cb5ac
files updated
MilenaChowaniec May 16, 2025
5898dd0
update
MilenaChowaniec May 16, 2025
225a775
added e2e tests
MilenaChowaniec May 20, 2025
877692c
changed plugin and e2e test
MilenaChowaniec Jul 11, 2025
5f13d49
run black to format code
MilenaChowaniec Jul 11, 2025
bae6b88
deleted unused Flask
MilenaChowaniec Jul 11, 2025
4c917ea
fix mistakes
MilenaChowaniec Jul 11, 2025
c66cf9d
deleted files
MilenaChowaniec Jul 11, 2025
7ed34e7
test
MilenaChowaniec Jul 31, 2025
7edbf27
lint fix
MilenaChowaniec Jul 31, 2025
10f6fd8
Merge branch 'main' into addedUnitTest
MilenaChowaniec Jul 31, 2025
eaa3f2d
fix issues
MilenaChowaniec Jul 31, 2025
d2a4359
lint fix
MilenaChowaniec Jul 31, 2025
8a934c9
update
MilenaChowaniec Jul 31, 2025
93e5c56
update
MilenaChowaniec Jul 31, 2025
910140a
update
MilenaChowaniec Aug 1, 2025
c061f9e
update
MilenaChowaniec Aug 1, 2025
3b4a6c7
update
MilenaChowaniec Aug 1, 2025
4a3c908
update
MilenaChowaniec Aug 1, 2025
32ab65d
update
MilenaChowaniec Aug 1, 2025
4991288
update
MilenaChowaniec Aug 7, 2025
9b5893b
update
MilenaChowaniec Sep 5, 2025
8e4efaa
tests fix
MilenaChowaniec Sep 5, 2025
56247d6
tests fix
MilenaChowaniec Sep 5, 2025
2f79ff4
e2e data dix
MilenaChowaniec Sep 5, 2025
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
31 changes: 20 additions & 11 deletions platzky_b4uleave/entrypoint.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
from flask import Flask, Response
from flask import Response
from typing import Any, Dict

def process(app, plugin_config: Dict[str, Any]): # Defines the main `process` function taking a Flask app instance and plugin configuration

def process(
app, plugin_config: Dict[str, Any]
): # Defines the main `process` function taking a Flask app instance and plugin configuration
# Store plugin configuration (defaults to empty dict)
app.config['b4uleave'] = plugin_config or {}
app.config["b4uleave"] = plugin_config or {}

message = app.config['b4uleave'].get('message', 'Czy na pewno chcesz<br>opuścić naszą stronę?')
stay = app.config['b4uleave'].get('stay', 'Stay')
leave = app.config['b4uleave'].get('leave', 'Leave')
message = app.config["b4uleave"].get(
"message", "Czy na pewno chcesz<br>opuścić naszą stronę?"
)
stay = app.config["b4uleave"].get("stay", "Stay")
leave = app.config["b4uleave"].get("leave", "Leave")

Comment on lines +11 to 16

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Unsanitised user-controlled HTML ⇒ reflected-XSS vector.

message, stay, and leave come straight from plugin_config and are interpolated into the DOM without escaping.
An attacker configuring the plugin (or compromising the config source) can inject arbitrary scripts.

-import ...
+import html  # at top

 ...
-message = app.config["b4uleave"].get(
-    "message", "Czy na pewno chcesz<br>opuścić naszą stronę?"
-)
-stay = app.config["b4uleave"].get("stay", "Stay")
-leave = app.config["b4uleave"].get("leave", "Leave")
+cfg      = app.config["b4uleave"]
+message  = html.escape(cfg.get("message", "Czy na pewno chcesz<br>opuścić naszą stronę?"), quote=True)
+stay     = html.escape(cfg.get("stay",   "Stay"),  quote=True)
+leave    = html.escape(cfg.get("leave",  "Leave"), quote=True)

If you intentionally allow HTML in message, use flask.Markup to whitelist only that field and still escape the others.

Also applies to: 69-73

🤖 Prompt for AI Agents
In platzky_b4uleave/entrypoint.py around lines 11 to 16, the variables message,
stay, and leave are taken directly from the config and inserted into the DOM
without escaping, creating a reflected XSS risk. To fix this, apply proper
escaping to stay and leave to prevent script injection, and if HTML is
intentionally allowed in message, wrap it with flask.Markup to safely mark it as
trusted HTML. Also apply the same escaping and marking approach to the similar
code at lines 69 to 73.

@app.after_request # Decorator that registers a function to run after each request is processed
@app.after_request # Decorator that registers a function to run after each request is processed
def add_B4ULeave(response: Response) -> Response:
if 'text/html' in response.headers.get('Content-Type', ''): # Function receives a Response object and returns a modified Response
if "text/html" in response.headers.get(
"Content-Type", ""
): # Function receives a Response object and returns a modified Response
html = f"""
<style>
#B4ULeave-ModalWindow {{
Expand Down Expand Up @@ -103,8 +110,10 @@ def add_B4ULeave(response: Response) -> Response:
}});
}})();
</script>"""
response.set_data(response.get_data(as_text=True).replace('</body>', html + '</body>'))
return response

response.set_data(
response.get_data(as_text=True).replace("</body>", html + "</body>")
)
Comment on lines +115 to +116

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

"</body>" string replace is brittle; use a real parser.

replace("</body>", html + "</body>")
• fails for <BODY> / mixed-case tags,
• injects nothing if the tag is missing,
• may break when the string appears inside a <script> or <textarea>.

Consider BeautifulSoup or lxml.html for robust insertion:

from bs4 import BeautifulSoup

soup = BeautifulSoup(response.get_data(as_text=True), "html.parser")
if soup.body:
    soup.body.append(BeautifulSoup(html, "html.parser"))
    response.set_data(str(soup))

At minimum, perform a case-insensitive search and fall back gracefully.

🤖 Prompt for AI Agents
In platzky_b4uleave/entrypoint.py around lines 115 to 116, the code uses a
brittle string replace to insert HTML before the closing </body> tag, which
fails with case variations, missing tags, or when the string appears inside
scripts. Replace this with a robust HTML parser like BeautifulSoup: parse the
response data, check for the body tag, append the new HTML safely, and then set
the modified HTML back to the response. This ensures correct insertion
regardless of tag case or content structure.

return response

return app
return app
Loading