mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-23 01:46:37 +08:00
feat(plugin): enforce certified archive admission (#2553)
* feat(plugin): add certified admission policy * feat(plugin): enforce certified archive admission * chore(plugin): pin certified SDK beta * fix(plugin): consume SDK beta 5 * style(plugin): format certification admission
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
import zipfile
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('deployment', 'certificate', 'force', 'expected_disposition', 'expected_code'),
|
||||
[
|
||||
('cloud', ('valid', 'shared-runtime-v1'), False, 'shared_eligible', 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE'),
|
||||
('cloud', ('absent', None), False, 'rejected', 'CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_REQUIRED'),
|
||||
('cloud', ('malformed', None), False, 'rejected', 'CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_INVALID'),
|
||||
('cloud', ('invalid', 'shared-runtime-v1'), True, 'rejected', 'CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_INVALID'),
|
||||
('oss', ('absent', None), False, 'dedicated_allowed', 'CERTIFIED_PLUGIN_OSS_LEGACY_DEDICATED'),
|
||||
('oss', ('valid', 'shared-runtime-v1'), False, 'shared_eligible', 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE'),
|
||||
('oss', ('invalid', 'shared-runtime-v1'), False, 'administrator_force_required', 'CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED'),
|
||||
('oss', ('invalid', 'shared-runtime-v1'), True, 'dedicated_allowed', 'CERTIFIED_PLUGIN_OSS_FORCED_DEDICATED'),
|
||||
],
|
||||
)
|
||||
def test_admission_policy_enforces_certification_matrix(
|
||||
deployment: str,
|
||||
certificate: tuple[str, str | None],
|
||||
force: bool,
|
||||
expected_disposition: str,
|
||||
expected_code: str,
|
||||
) -> None:
|
||||
from langbot.pkg.plugin.certification import (
|
||||
CertificateFacts,
|
||||
CertificateVerification,
|
||||
PluginCertificationFacts,
|
||||
decide_plugin_admission,
|
||||
)
|
||||
|
||||
verification, runtime_profile = certificate
|
||||
facts = PluginCertificationFacts(
|
||||
installation_uuid='00000000-0000-4000-8000-000000000001',
|
||||
artifact_digest='a' * 64,
|
||||
certificate=CertificateFacts(
|
||||
verification=CertificateVerification(verification),
|
||||
runtime_profile=runtime_profile,
|
||||
),
|
||||
)
|
||||
|
||||
decision = decide_plugin_admission(
|
||||
deployment=deployment,
|
||||
facts=facts,
|
||||
administrator_force=force,
|
||||
)
|
||||
|
||||
assert decision.disposition.value == expected_disposition
|
||||
assert decision.code.value == expected_code
|
||||
assert decision.runtime_profile == (
|
||||
'shared-runtime-v1' if expected_disposition == 'shared_eligible' else 'dedicated'
|
||||
)
|
||||
|
||||
|
||||
def test_archive_inspection_preserves_legacy_tuple_and_exposes_certificate_facts() -> None:
|
||||
from langbot.pkg.plugin.archive import (
|
||||
ArchiveCertificateState,
|
||||
inspect_plugin_archive,
|
||||
inspect_plugin_archive_metadata,
|
||||
)
|
||||
|
||||
manifest = {
|
||||
'kind': 'Plugin',
|
||||
'metadata': {'name': 'example'},
|
||||
'certification': {
|
||||
'runtime_profile': 'shared-runtime-v1',
|
||||
'certificate': {'issuer': 'sdk-test', 'signature': 'not-verified-by-core'},
|
||||
},
|
||||
}
|
||||
archive_bytes = _archive_bytes(manifest)
|
||||
|
||||
inspection = inspect_plugin_archive(archive_bytes)
|
||||
|
||||
assert inspection.artifact_digest == hashlib.sha256(archive_bytes).hexdigest()
|
||||
assert inspection.certificate.state is ArchiveCertificateState.DECLARED
|
||||
assert inspection.certificate.runtime_profile == 'shared-runtime-v1'
|
||||
assert inspection.certificate.payload == manifest['certification']['certificate']
|
||||
assert inspect_plugin_archive_metadata(archive_bytes) == (
|
||||
inspection.manifest,
|
||||
inspection.requirements,
|
||||
inspection.names,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('certification', 'expected_state'),
|
||||
[
|
||||
(None, 'absent'),
|
||||
({'runtime_profile': 123, 'certificate': {}}, 'malformed'),
|
||||
({'runtime_profile': 'shared-runtime-v1'}, 'malformed'),
|
||||
],
|
||||
)
|
||||
def test_archive_inspection_reports_nonverifying_certificate_states(
|
||||
certification: object,
|
||||
expected_state: str,
|
||||
) -> None:
|
||||
from langbot.pkg.plugin.archive import inspect_plugin_archive
|
||||
|
||||
manifest: dict[str, object] = {'kind': 'Plugin', 'metadata': {'name': 'example'}}
|
||||
if certification is not None:
|
||||
manifest['certification'] = certification
|
||||
|
||||
inspection = inspect_plugin_archive(_archive_bytes(manifest))
|
||||
|
||||
assert inspection.certificate.state.value == expected_state
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('verification', 'expected_visibility'),
|
||||
[
|
||||
('valid', 'tenant_scoped'),
|
||||
('absent', 'detailed_process'),
|
||||
('malformed', 'detailed_process'),
|
||||
('invalid', 'detailed_process'),
|
||||
],
|
||||
)
|
||||
def test_log_visibility_policy_only_scopes_valid_shared_certifications(
|
||||
verification: str,
|
||||
expected_visibility: str,
|
||||
) -> None:
|
||||
from langbot.pkg.plugin.certification import (
|
||||
CertificateFacts,
|
||||
CertificateVerification,
|
||||
PluginCertificationFacts,
|
||||
decide_plugin_log_visibility,
|
||||
)
|
||||
|
||||
facts = PluginCertificationFacts(
|
||||
installation_uuid='00000000-0000-4000-8000-000000000001',
|
||||
artifact_digest='a' * 64,
|
||||
certificate=CertificateFacts(
|
||||
verification=CertificateVerification(verification),
|
||||
runtime_profile='shared-runtime-v1',
|
||||
),
|
||||
)
|
||||
|
||||
visibility = decide_plugin_log_visibility(facts)
|
||||
|
||||
assert visibility.value == expected_visibility
|
||||
|
||||
|
||||
def _archive_bytes(manifest: dict[str, object]) -> bytes:
|
||||
buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(buffer, 'w') as archive:
|
||||
import yaml
|
||||
|
||||
archive.writestr('manifest.yaml', yaml.safe_dump(manifest))
|
||||
return buffer.getvalue()
|
||||
@@ -272,6 +272,12 @@ async def test_local_install_persists_verified_package_before_runtime_apply():
|
||||
connector._inspect_plugin_package = Mock(return_value=('author', 'plugin'))
|
||||
connector._store_artifact_package = AsyncMock()
|
||||
connector._persist_installation_package = AsyncMock(return_value=(binding, None, False))
|
||||
connector._admit_plugin_archive = Mock(
|
||||
return_value=(
|
||||
{'_certification': {'normalized_digest': digest}},
|
||||
SimpleNamespace(for_installation=lambda _installation_uuid: SimpleNamespace(artifact_digest=digest)),
|
||||
)
|
||||
)
|
||||
connector._wait_for_installed_plugin_ready = AsyncMock()
|
||||
|
||||
await connector.install_plugin(
|
||||
|
||||
Reference in New Issue
Block a user