Skip to content

Commit

Permalink
Merge pull request #8946 from OpenMined/fix/sanitize-html
Browse files Browse the repository at this point in the history
fix: sanitize html output
  • Loading branch information
koenvanderveen authored Jun 21, 2024
2 parents cc9627d + e97e735 commit 8bd3611
Show file tree
Hide file tree
Showing 13 changed files with 386 additions and 513 deletions.
1 change: 1 addition & 0 deletions packages/syft/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ syft =
rich==13.7.1
jinja2==3.1.4
tenacity==8.3.0
nh3==0.2.17

install_requires =
%(syft)s
Expand Down
99 changes: 2 additions & 97 deletions packages/syft/src/syft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import pathlib
from pathlib import Path
import sys
from types import MethodType
from typing import Any

# relative
Expand Down Expand Up @@ -74,14 +73,14 @@
from .service.user.roles import Roles as roles # noqa: F401
from .service.user.user_service import UserService # noqa: F401
from .stable_version import LATEST_STABLE_SYFT
from .types.syft_object import SyftObject
from .types.twin_object import TwinObject # noqa: F401
from .types.uid import UID # noqa: F401
from .util import filterwarnings # noqa: F401
from .util import logger # noqa: F401
from .util import options # noqa: F401
from .util.autoreload import disable_autoreload # noqa: F401
from .util.autoreload import enable_autoreload # noqa: F401
from .util.patch_ipython import patch_ipython
from .util.telemetry import instrument # noqa: F401
from .util.util import autocache # noqa: F401
from .util.util import get_root_data_path # noqa: F401
Expand All @@ -97,101 +96,7 @@

logger.start()

try:
# third party
from IPython import get_ipython

get_ipython() # noqa: F821
# TODO: add back later or auto detect
# display(
# Markdown(
# "\nWarning: syft is imported in light mode by default. \
# \nTo switch to dark mode, please run `sy.options.color_theme = 'dark'`"
# )
# )
except: # noqa: E722
pass # nosec


def _patch_ipython_autocompletion() -> None:
try:
# third party
from IPython.core.guarded_eval import EVALUATION_POLICIES
except ImportError:
return

ipython = get_ipython()
if ipython is None:
return

try:
# this allows property getters to be used in nested autocomplete
ipython.Completer.evaluation = "limited"
ipython.Completer.use_jedi = False
policy = EVALUATION_POLICIES["limited"]

policy.allowed_getattr_external.update(
[
("syft.client.api", "APIModule"),
("syft.client.api", "SyftAPI"),
]
)
original_can_get_attr = policy.can_get_attr

def patched_can_get_attr(value: Any, attr: str) -> bool:
attr_name = "__syft_allow_autocomplete__"
# first check if exist to prevent side effects
if hasattr(value, attr_name) and attr in getattr(value, attr_name, []):
if attr in dir(value):
return True
else:
return False
else:
return original_can_get_attr(value, attr)

policy.can_get_attr = patched_can_get_attr
except Exception:
print("Failed to patch ipython autocompletion for syft property getters")

try:
# this constraints the completions for autocomplete.
# if __syft_dir__ is defined we only autocomplete those properties
# stdlib
import re

original_attr_matches = ipython.Completer.attr_matches

def patched_attr_matches(self, text: str) -> list[str]: # type: ignore
res = original_attr_matches(text)
m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
if not m2:
return res
expr, _ = m2.group(1, 2)
obj = self._evaluate_expr(expr)
if isinstance(obj, SyftObject) and hasattr(obj, "__syft_dir__"):
# here we filter all autocomplete results to only contain those
# defined in __syft_dir__, however the original autocomplete prefixes
# have the full path, while __syft_dir__ only defines the attr
attrs = set(obj.__syft_dir__())
new_res = []
for r in res:
splitted = r.split(".")
if len(splitted) > 1:
attr_name = splitted[-1]
if attr_name in attrs:
new_res.append(r)
return new_res
else:
return res

ipython.Completer.attr_matches = MethodType(
patched_attr_matches, ipython.Completer
)
except Exception:
print("Failed to patch syft autocompletion for __syft_dir__")


_patch_ipython_autocompletion()
patch_ipython()


def module_property(func: Any) -> Callable:
Expand Down
51 changes: 51 additions & 0 deletions packages/syft/src/syft/assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ body.vscode-dark {
--tertiary-color: #cfcdd6;
--button-color: #111111;
--colors-black: #ffffff;
--surface-color: #fff;
}

body {
Expand All @@ -13,6 +14,7 @@ body {
--tertiary-color: #000000de;
--button-color: #d1d5db;
--colors-black: #17161d;
--surface-color: #464158;
}

.header-1 {
Expand Down Expand Up @@ -564,3 +566,52 @@ body {
.syft-widget li a:hover {
background-color: #c2def0;
}

.syft-user_code,
.syft-project,
.syft-project-create,
.syft-settings,
.syft-dataset,
.syft-asset,
.syft-contributor,
.syft-request,
.syft-syncstate,
.job-info {
color: var(--surface-color);
}

.syft-dataset h3,
.syft-dataset p,
.syft-asset h3,
.syft-asset p,
.syft-syncstate h3,
.syft-syncstate p {
font-family: "Open Sans";
}

.diff-container {
border: 0.5px solid #b4b0bf;
}

.syft-container {
padding: 5px;
font-family: "Open Sans";
}

.syft-alert-info {
color: #1f567a;
background-color: #c2def0;
border-radius: 4px;
padding: 5px;
padding: 13px 10px;
}

.syft-code-block {
background-color: #f7f7f7;
border: 1px solid #cfcfcf;
padding: 0px 2px;
}

.syft-space {
margin-top: 1em;
}
6 changes: 2 additions & 4 deletions packages/syft/src/syft/assets/svg/copy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions packages/syft/src/syft/client/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
from ..types.uid import UID
from ..util.autoreload import autoreload_enabled
from ..util.markdown import as_markdown_python_code
from ..util.table import list_dict_repr_html
from ..util.notebook_ui.components.tabulator_template import build_tabulator_table
from ..util.telemetry import instrument
from ..util.util import prompt_warning_message
from .connection import NodeConnection
Expand Down Expand Up @@ -730,9 +730,9 @@ def recursively_get_submodules(
APISubModulesView(submodule=submodule_name, endpoints=child_paths)
)

return list_dict_repr_html(views)
# return NotImplementedError
return build_tabulator_table(views)

# should never happen?
results = self.get_all()
return results._repr_html_()

Expand Down
29 changes: 20 additions & 9 deletions packages/syft/src/syft/service/code_history/code_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
from ...types.syft_object import SyftObject
from ...types.syft_object import SyftVerifyKey
from ...types.uid import UID
from ...util.notebook_ui.components.table_template import create_table_template
from ...util.notebook_ui.components.tabulator_template import (
build_tabulator_table_with_data,
)
from ...util.table import prepare_table_data
from ..code.user_code import UserCode
from ..response import SyftError
Expand Down Expand Up @@ -54,18 +56,21 @@ class CodeHistoryView(SyftObject):
def _coll_repr_(self) -> dict[str, int]:
return {"Number of versions": len(self.user_code_history)}

def _repr_html_(self) -> str:
# TODO techdebt: move this to _coll_repr_
rows, _ = prepare_table_data(self.user_code_history)
def _repr_html_(self) -> str | None:
rows, metadata = prepare_table_data(self.user_code_history)

for i, r in enumerate(rows):
r["Version"] = f"v{i}"
raw_code = self.user_code_history[i].raw_code
n_code_lines = raw_code.count("\n")
if n_code_lines > 5:
raw_code = "\n".join(raw_code.split("\n", 5))
r["Code"] = raw_code
# rows = sorted(rows, key=lambda x: x["Version"])
return create_table_template(rows, "CodeHistory", icon=None)

metadata["name"] = "Code History"
metadata["columns"] += ["Version", "Code"]

return build_tabulator_table_with_data(rows, metadata)

def __getitem__(self, index: int | str) -> UserCode | SyftError:
if isinstance(index, str):
Expand Down Expand Up @@ -136,8 +141,14 @@ def __getitem__(self, key: str | int) -> CodeHistoriesDict | SyftError:
)
return api.services.code_history.get_history_for_user(key)

def _repr_html_(self) -> str:
def _repr_html_(self) -> str | None:
rows = [
{"user": user, "UserCodes": funcs} for user, funcs in self.user_dict.items()
{"User": user, "UserCodes": ", ".join(funcs)}
for user, funcs in self.user_dict.items()
]
return create_table_template(rows, "UserCodeHistory", icon=None)
metadata = {
"name": "UserCode Histories",
"columns": ["User", "UserCodes"],
"icon": None,
}
return build_tabulator_table_with_data(rows, metadata)
5 changes: 3 additions & 2 deletions packages/syft/src/syft/service/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# relative
from ..serde.serializable import serializable
from ..types.base import SyftBaseModel
from ..util.util import sanitize_html


class SyftResponseMessage(SyftBaseModel):
Expand Down Expand Up @@ -44,7 +45,7 @@ def _repr_html_(self) -> str:
f'<div class="{self._repr_html_class_}" style="padding:5px;">'
f"<strong>{type(self).__name__}</strong>: "
f'<pre class="{self._repr_html_class_}" style="display:inline; font-family:inherit;">'
f"{self.message}</pre></div><br/>"
f"{sanitize_html(self.message)}</pre></div><br/>"
)


Expand Down Expand Up @@ -107,7 +108,7 @@ def _repr_html_class_(self) -> str:
def _repr_html_(self) -> str:
return (
f'<div class="{self._repr_html_class_}" style="padding:5px;">'
+ f"<strong>{type(self).__name__}</strong>: {self.args}</div><br />"
+ f"<strong>{type(self).__name__}</strong>: {sanitize_html(self.args)}</div><br />"
)

@staticmethod
Expand Down
Loading

0 comments on commit 8bd3611

Please sign in to comment.