Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 11 additions & 1 deletion docs/source/kernel-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,26 @@ maintain an older `model`-type kernel repository.

`kernels` only loads kernels from a curated set of trusted publishers by
default. Loading from any other publisher raises an error unless the caller
opts in with `trust_remote_code=True`:
opts in with `trust_remote_code=True` or explicitly allowlists the repository:

```python
# Trusted publisher: works without opt-in.
get_kernel("kernels-community/activation", version=1)

# Untrusted publisher: must opt in explicitly.
get_kernel("some-other-org/my-kernel", version=1, trust_remote_code=True)

# Allow only specific repositories from untrusted publishers.
get_kernel(
"some-other-org/my-kernel",
version=1,
trust_remote_code=["some-other-org/my-kernel"],
)
```

The repository IDs in the allowlist must match exactly. Other repositories
from the same publisher remain subject to the default trust check.

The Hub also exposes a `trustedKernelPublisher` flag on the kernel API and
displays a corresponding badge in the UI.

Expand Down
23 changes: 9 additions & 14 deletions kernels/src/kernels/hf_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,15 @@ def _check_trust_remote_code(repo_id: str, local_files_only: bool, trust_remote_

When ``trust_remote_code`` is ``True``, all repositories are allowed.

When ``trust_remote_code`` is a list of strings, it is treated as a list
of signing identities to verify against. Signing verification is not yet
implemented, so passing a list currently emits a warning and falls back
to the default trust check (i.e. only trusted publishers are allowed).
When ``trust_remote_code`` is a list of strings, it is treated as an
allowlist of repository IDs. Only repositories in the list and repositories
from trusted publishers are allowed.
"""
if trust_remote_code is True:
return

if isinstance(trust_remote_code, list):
warnings.warn(
"Signing identity verification is not yet implemented. "
"The provided signing identities will be ignored and the "
"kernel will be treated as untrusted. Use trust_remote_code=True "
"to bypass trust checks.",
stacklevel=3,
)
if isinstance(trust_remote_code, list) and repo_id in trust_remote_code:
return

if local_files_only:
# Publisher trust cannot be verified offline. The user opted into
Expand All @@ -136,13 +129,15 @@ def _check_trust_remote_code(repo_id: str, local_files_only: bool, trust_remote_
except Exception:
raise ValueError(
f"Kernel repository '{repo_id}' could not verify publisher trust status. "
"Set trust_remote_code=True to allow loading kernels from untrusted sources."
"Set trust_remote_code=True or add the repository ID to the trust_remote_code allowlist "
"to allow loading kernels from untrusted sources."
)

if getattr(info, "trustedKernelPublisher", False):
return

raise ValueError(
f"Kernel repository '{repo_id}' is not from a trusted publisher. "
"Set trust_remote_code=True to allow loading kernels from untrusted sources."
"Set trust_remote_code=True or add the repository ID to the trust_remote_code allowlist "
"to allow loading kernels from untrusted sources."
)
5 changes: 2 additions & 3 deletions kernels/src/kernels/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ def install_kernel(
trust_remote_code (`bool | list[str]`, *optional*, defaults to `False`):
Whether to allow loading kernels from untrusted organisations. When ``False``,
only kernels from trusted organisations are allowed. When ``True``, all
repositories are allowed. A list of strings will be used to verify signing
identities in a future release; for now it emits a warning and falls
back to the default trust check.
repositories are allowed. A list of repository IDs allows only those
repositories in addition to repositories from trusted organisations.

Returns:
`Path`: The path to the variant directory.
Expand Down
16 changes: 12 additions & 4 deletions kernels/src/kernels/layer/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def __init__(

self._repo_id = repo_id
self.func_name = func_name
self._trust_remote_code = trust_remote_code
self._trust_remote_code = (
trust_remote_code.copy() if isinstance(trust_remote_code, list) else trust_remote_code
)
Comment on lines +97 to +99

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This complexity is for the following reasons:

  • We use a check of if isinstance(trust_remote_code, list): .... Otherwise, we could have packed into a tuple directly. We need a tuple to be able to hash().
  • We want to prevent user-side mutations to an allowlist affecting the status of trust_remote_code.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like the defensive coding here!


# We are going to resolve these lazily, since we do not want
# to do a network request for every registered FuncRepository.
Expand Down Expand Up @@ -135,7 +137,9 @@ def __hash__(self):
self._repo_id,
self._revision,
self._version,
self._trust_remote_code,
tuple(self._trust_remote_code)
if isinstance(self._trust_remote_code, list)
else self._trust_remote_code,
)
)

Expand Down Expand Up @@ -304,7 +308,9 @@ def __init__(
self._repo_id = repo_id
self._lockfile = lockfile
self.func_name = func_name
self._trust_remote_code = trust_remote_code
self._trust_remote_code = (
trust_remote_code.copy() if isinstance(trust_remote_code, list) else trust_remote_code
)
kernel_locks, kernel_dep = self._get_lock()
self.kernel_locks = kernel_locks
self.kernel_dep = kernel_dep
Expand Down Expand Up @@ -353,7 +359,9 @@ def __hash__(self):
self._repo_id,
self.kernel_dep,
self.kernel_locks,
self._trust_remote_code,
tuple(self._trust_remote_code)
if isinstance(self._trust_remote_code, list)
else self._trust_remote_code,
)
)

Expand Down
23 changes: 16 additions & 7 deletions kernels/src/kernels/layer/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ class LayerRepository:
The kernel version to download. Cannot be used together with `revision`.
Either `version` or `revision` must be specified.
trust_remote_code (`bool | list[str]`, *optional*, defaults to `False`):
Whether to allow loading kernels from untrusted organisations. A list
of signing identities can be provided for future verification support;
until then it warns and falls back to the default trust check.
Whether to allow loading kernels from untrusted organisations. When `False`,
only kernels from trusted organisations are allowed. When `True`, all
repositories are allowed. A list of repository IDs allows only those
repositories in addition to repositories from trusted organisations.

Example:
```python
Expand Down Expand Up @@ -88,7 +89,9 @@ def __init__(

self._repo_id = repo_id
self.layer_name = layer_name
self._trust_remote_code = trust_remote_code
self._trust_remote_code = (
trust_remote_code.copy() if isinstance(trust_remote_code, list) else trust_remote_code
)

# We are going to resolve these lazily, since we do not want
# to do a network request for every registered LayerRepository.
Expand Down Expand Up @@ -129,7 +132,9 @@ def __hash__(self):
self._repo_id,
self._revision,
self._version,
self._trust_remote_code,
tuple(self._trust_remote_code)
if isinstance(self._trust_remote_code, list)
else self._trust_remote_code,
)
)

Expand Down Expand Up @@ -213,7 +218,9 @@ def __init__(
self._repo_id = repo_id
self._lockfile = lockfile
self.layer_name = layer_name
self._trust_remote_code = trust_remote_code
self._trust_remote_code = (
trust_remote_code.copy() if isinstance(trust_remote_code, list) else trust_remote_code
)
kernel_locks, kernel_dep = self._get_lock()
self.kernel_locks = kernel_locks
self.kernel_dep = kernel_dep
Expand Down Expand Up @@ -262,7 +269,9 @@ def __hash__(self):
self._repo_id,
self.kernel_dep,
self.kernel_locks,
self._trust_remote_code,
tuple(self._trust_remote_code)
if isinstance(self._trust_remote_code, list)
else self._trust_remote_code,
)
)

Expand Down
15 changes: 6 additions & 9 deletions kernels/src/kernels/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,8 @@ def get_kernel(
trust_remote_code (`bool | list[str]`, *optional*, defaults to `False`):
Whether to allow loading kernels from untrusted organisations. When ``False``,
only kernels from trusted organisations are allowed. When ``True``, all
repositories are allowed. A list of strings will be used to verify signing
identities in a future release; for now it emits a warning and falls
back to the default trust check.
repositories are allowed. A list of repository IDs allows only those
repositories in addition to repositories from trusted organisations.
check_arch (`bool`, *optional*, defaults to `True`):
Whether to check that the kernel build supports the architecture
(e.g. CUDA compute capability) of the current device. Kernels can
Expand Down Expand Up @@ -198,9 +197,8 @@ def get_local_kernel(
trust_remote_code (`bool | list[str]`, *optional*, defaults to `False`):
Whether to allow loading kernels from untrusted organisations. When ``False``,
only kernels from trusted organisations are allowed. When ``True``, all
repositories are allowed. A list of strings will be used to verify signing
identities in a future release; for now it emits a warning and falls
back to the default trust check.
repositories are allowed. A list of repository IDs allows only those
repositories in addition to repositories from trusted organisations.



Expand Down Expand Up @@ -257,9 +255,8 @@ def has_kernel(
trust_remote_code (`bool | list[str]`, *optional*, defaults to `False`):
Whether to allow loading kernels from untrusted organisations. When ``False``,
only kernels from trusted organisations are allowed. When ``True``, all
repositories are allowed. A list of strings will be used to verify signing
identities in a future release; for now it emits a warning and falls
back to the default trust check.
repositories are allowed. A list of repository IDs allows only those
repositories in addition to repositories from trusted organisations.
check_arch (`bool`, *optional*, defaults to `True`):
Whether to check that the kernel build supports the architecture
(e.g. CUDA compute capability) of the current device. Kernels can
Expand Down
16 changes: 16 additions & 0 deletions kernels/tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,22 @@ def test_trust_remote_code_flag_allows_untrusted():
get_kernel("kernels-test-untrusted/ci-test-kernel", version=1, trust_remote_code=True)


def test_trust_remote_code_allowlist_allows_untrusted():
"""An allowlist should bypass the org check for repositories it contains."""
repo_id = "kernels-test-untrusted/ci-test-kernel"
get_kernel(repo_id, version=1, trust_remote_code=[repo_id])


def test_trust_remote_code_allowlist_blocks_unlisted():
"""A non-empty allowlist should not bypass the org check for other repositories."""
with pytest.raises(ValueError, match=r"not from a trusted publisher"):
get_kernel(
"kernels-test-untrusted/not-a-trused-org-kernel",
version=1,
trust_remote_code=["kernels-test-untrusted/ci-test-kernel"],
)


def test_install_kernel_offline_with_revision(local_kernel_path):
"""install_kernel should resolve a cached snapshot when HF_HUB_OFFLINE=1."""
expected_path = local_kernel_path
Expand Down
27 changes: 27 additions & 0 deletions kernels/tests/test_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,33 @@ def test_layer_repository_requires_version_or_revision():
LayerRepository(repo_id="kernels-test/silu-and-mul", layer_name="SiluAndMul")


def test_layer_repository_with_trust_remote_code_allowlist_is_hashable():
allowlist = ["untrusted-org/allowed-kernel"]
repo = LayerRepository(
repo_id="untrusted-org/allowed-kernel",
layer_name="SiluAndMul",
revision="main",
trust_remote_code=allowlist,
)

original_hash = hash(repo)
allowlist.append("untrusted-org/another-kernel")

assert hash(repo) == original_hash


def test_layer_repository_trust_remote_code_allowlist_blocks_unlisted():
repo = LayerRepository(
repo_id="kernels-test-untrusted/not-a-trused-org-kernel",
layer_name="SiluAndMul",
revision="main",
trust_remote_code=["kernels-test-untrusted/ci-test-kernel"],
)

with pytest.raises(ValueError, match=r"not from a trusted publisher"):
repo.load()


def test_validate_kernel_layer():
class BadLayer(nn.Module):
def __init__(self, *args, **kwargs):
Expand Down
Loading