mirror of
https://github.com/usestrix/strix.git
synced 2026-10-10 03:28:11 +00:00
Merge 8e3c7aee7a into 03d2c6c168
This commit is contained in:
commit
b825459c55
3 changed files with 141 additions and 0 deletions
33
.github/pull_request_template.md
vendored
Normal file
33
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
## Description
|
||||
|
||||
<!-- Provide a brief description of your changes -->
|
||||
|
||||
## Type of Change
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
- [ ] Documentation update
|
||||
- [ ] Dependency update
|
||||
|
||||
## Related Issue
|
||||
|
||||
<!-- Link to the issue this PR addresses (e.g., Fixes #1234) -->
|
||||
|
||||
## Testing
|
||||
|
||||
- [ ] Unit tests added/updated
|
||||
- [ ] Integration tests added/updated
|
||||
- [ ] Manual testing completed
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Code follows project style guidelines
|
||||
- [ ] Documentation has been updated
|
||||
- [ ] No new warnings generated
|
||||
- [ ] Tests pass locally
|
||||
- [ ] Changes are backwards compatible
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!-- Add any other relevant information here -->
|
||||
85
strix/interface/cloud/domain_verifier.py
Normal file
85
strix/interface/cloud/domain_verifier.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
"""Classify provider-managed domains for cloud verification.
|
||||
|
||||
Provider default hostnames are not proof of ownership by themselves. This
|
||||
module only selects the verification strategy; the managed API remains the
|
||||
authority that checks the authenticated provider integration.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Final
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ProviderDomain:
|
||||
"""Metadata for a provider-owned default hostname suffix."""
|
||||
|
||||
provider: str
|
||||
requires_dns: bool = False
|
||||
|
||||
|
||||
PROVIDER_DEFAULT_DOMAINS: Final[dict[str, ProviderDomain]] = {
|
||||
"vercel.app": ProviderDomain("vercel"),
|
||||
"netlify.app": ProviderDomain("netlify"),
|
||||
"github.io": ProviderDomain("github"),
|
||||
"herokuapp.com": ProviderDomain("heroku"),
|
||||
}
|
||||
|
||||
|
||||
def _normalise(domain: str | None) -> str:
|
||||
"""Return a comparable hostname, without a trailing root dot."""
|
||||
if not isinstance(domain, str):
|
||||
return ""
|
||||
return domain.strip().lower().rstrip(".")
|
||||
|
||||
|
||||
def get_provider_info(domain: str | None) -> ProviderDomain | None:
|
||||
"""Return provider metadata when *domain* is a provider default hostname.
|
||||
|
||||
Matching is label-aware: ``notvercel.app`` and ``example.vercel.app.evil``
|
||||
do not match ``vercel.app``.
|
||||
"""
|
||||
hostname = _normalise(domain)
|
||||
if not hostname or any(len(label) > 63 for label in hostname.split(".")):
|
||||
return None
|
||||
for suffix, info in PROVIDER_DEFAULT_DOMAINS.items():
|
||||
if hostname.endswith(f".{suffix}"):
|
||||
return info
|
||||
return None
|
||||
|
||||
|
||||
def is_provider_default_domain(domain: str | None) -> bool:
|
||||
"""Return whether *domain* is hosted under a known provider suffix."""
|
||||
return get_provider_info(domain) is not None
|
||||
|
||||
|
||||
def validate_domain_verification_method(domain: str | None) -> tuple[bool, str]:
|
||||
"""Select ``provider`` for default hostnames and ``dns`` otherwise."""
|
||||
hostname = _normalise(domain)
|
||||
if not hostname or any(not label for label in hostname.split(".")):
|
||||
return False, "unknown"
|
||||
info = get_provider_info(hostname)
|
||||
if info is not None and not info.requires_dns:
|
||||
return True, "provider"
|
||||
return True, "dns"
|
||||
|
||||
|
||||
async def verify_provider_domain(
|
||||
domain: str | None,
|
||||
provider_token: str | None = None,
|
||||
) -> tuple[bool, str]:
|
||||
"""Return the provider verification decision for API callers.
|
||||
|
||||
The CLI cannot prove that a hostname belongs to the user without calling
|
||||
the managed API. ``provider_token`` is intentionally accepted for API
|
||||
integrations, but is never logged or sent anywhere by this helper.
|
||||
"""
|
||||
del provider_token
|
||||
info = get_provider_info(domain)
|
||||
if info is None:
|
||||
return False, f"Domain {domain} is not a recognized provider domain"
|
||||
return (
|
||||
True,
|
||||
f"{info.provider} domain {domain} requires provider-account verification",
|
||||
)
|
||||
23
tests/test_domain_verification.py
Normal file
23
tests/test_domain_verification.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"""Tests for provider default-domain classification."""
|
||||
|
||||
from strix.interface.cloud.domain_verifier import (
|
||||
get_provider_info,
|
||||
is_provider_default_domain,
|
||||
validate_domain_verification_method,
|
||||
)
|
||||
|
||||
|
||||
def test_provider_domains_are_case_insensitive_and_label_aware() -> None:
|
||||
assert is_provider_default_domain("Demo.Vercel.App.")
|
||||
assert is_provider_default_domain("user.github.io")
|
||||
assert not is_provider_default_domain("notvercel.app")
|
||||
assert not is_provider_default_domain("demo.vercel.app.evil.example")
|
||||
|
||||
|
||||
def test_provider_info_and_verification_method() -> None:
|
||||
info = get_provider_info("demo.vercel.app")
|
||||
assert info is not None
|
||||
assert info.provider == "vercel"
|
||||
assert validate_domain_verification_method("demo.vercel.app") == (True, "provider")
|
||||
assert validate_domain_verification_method("example.com") == (True, "dns")
|
||||
assert validate_domain_verification_method("") == (False, "unknown")
|
||||
Loading…
Add table
Reference in a new issue