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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ under **Unreleased** until a new version is selected and published.

### Fixed

- Preserved fail-closed distribution provenance across version parsing,
brandless FE/BE component fallback, and certification evidence validation,
preventing unknown or mismatched brands from inheriting Apache Doris
certification.
- Updated the locked Marshmallow and Virtualenv transitive dependencies to
releases that address CVE-2025-68480 and symlink-based TOCTOU vulnerabilities
in development-environment creation.
Expand Down
4 changes: 3 additions & 1 deletion doris_mcp_server/tools/capability_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2450,7 +2450,9 @@ def _component_version(
# Brandless component builds (for example "4.0.6" or "4.0.6-abc1234")
# carry no brand of their own; inherit the cluster brand observed in
# @@version_comment so distribution provenance is not lost.
brand = default_brand or "doris"
if default_brand is None:
return parsed
brand = default_brand
return parse_doris_version_comment(f"{brand} version {brand}-{raw}")


Expand Down
45 changes: 20 additions & 25 deletions doris_mcp_server/tools/doris_feature_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,29 +462,19 @@ def _validate_evidence(self) -> Self:
*self.backend_version_comments,
)
for comment in comments:
if self.brand == "doris":
observed = parse_doris_version_comment(comment)
if (
not observed.is_parsed
or observed.core != target_literal
):
raise ValueError(
"every certified FE and BE must report the Doris core "
f"version {target_literal}"
)
else:
# Distribution evidence is validated without relying on the
# runtime brand registry: the comment must carry the declared
# brand token and the certified three-part core version.
if not _distribution_comment_matches(
comment,
brand=self.brand,
core=target_literal,
):
raise ValueError(
f"every certified FE and BE must report the {self.brand} "
f"core version {target_literal}"
)
# Evidence validation is independent of the mutable runtime brand
# registry. Every comment must carry the exact declared brand and
# three-part core version before it can certify that distribution.
if not _certification_comment_matches(
comment,
brand=self.brand,
core=target_literal,
):
display_brand = "Doris" if self.brand == "doris" else self.brand
raise ValueError(
f"every certified FE and BE must report the {display_brand} "
f"core version {target_literal}"
)

expected_cases = {
("stdio", "hierarchical"),
Expand Down Expand Up @@ -990,19 +980,24 @@ def _ordered_unique(values: Iterable[object]) -> tuple[str, ...]:
return tuple(dict.fromkeys(str(value) for value in values))


def _distribution_comment_matches(
def _certification_comment_matches(
comment: str,
*,
brand: str,
core: str,
) -> bool:
"""Match one evidence brand and core without the mutable alias registry."""
escaped_brand = re.escape(brand)
leading_brand = (
rf"(?:apache\s+{escaped_brand}|{escaped_brand})"
if brand == "doris"
else escaped_brand
)
escaped_core = re.escape(core)
pattern = re.compile(
rf"""
(?<![A-Za-z0-9_])
{escaped_brand}
{leading_brand}
(?:\s*,?\s*version)?
(?:\s+{escaped_brand}-|\s*-\s*|\s+)
{escaped_core}
Expand Down
15 changes: 11 additions & 4 deletions doris_mcp_server/tools/doris_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,16 @@


def _compile_version_pattern(brands: tuple[str, ...]) -> re.Pattern[str]:
brand_alternatives = "|".join((r"apache\s+doris", *brands))
prefix_alternatives = "|".join(brands)
brand_alternatives = "|".join(
(r"apache\s+doris", *(re.escape(brand) for brand in brands))
)
prefix_alternatives = "|".join(re.escape(brand) for brand in brands)
return re.compile(
rf"""
(?<![A-Za-z0-9_])
(?P<brand>{brand_alternatives})
(?:\s*,?\s*version)?
(?:\s+(?:{prefix_alternatives})-|\s*-\s*|\s+)
(?:\s+(?P<version_brand>{prefix_alternatives})-|\s*-\s*|\s+)
(?P<core>\d+\.\d+\.\d+)
(?:-(?P<prerelease>rc\d+|alpha\d*|beta\d*))?
(?:-(?P<commit>[0-9a-f]{{7,40}}))?
Expand Down Expand Up @@ -161,6 +163,11 @@ def parse_doris_version_comment(comment: str) -> DorisVersion:
if match is None:
return DorisVersion(raw=comment, deployment_hint=deployment_hint)

brand = _normalize_brand(match.group("brand"))
version_brand = match.group("version_brand")
if version_brand is not None and _normalize_brand(version_brand) != brand:
return DorisVersion(raw=comment, deployment_hint=deployment_hint)

major, minor, patch = (int(part) for part in match.group("core").split("."))
prerelease = match.group("prerelease")
commit = match.group("commit")
Expand All @@ -173,7 +180,7 @@ def parse_doris_version_comment(comment: str) -> DorisVersion:
prerelease=prerelease.lower() if prerelease else None,
commit=commit.lower() if commit else None,
deployment_hint=deployment_hint,
brand=_normalize_brand(match.group("brand")),
brand=brand,
parse_status=DorisVersionParseStatus.PARSED,
)

Expand Down
47 changes: 47 additions & 0 deletions test/tools/test_capability_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,53 @@ async def test_detector_propagates_comment_brand_to_brandless_components(
assert report.certified is False


@pytest.mark.asyncio
async def test_unknown_brand_keeps_brandless_components_unknown() -> None:
connection = _ProbeConnection()
connection.row_overrides["SELECT @@version_comment;"] = [
{"@@version_comment": "unregistereddb version 4.0.5"}
]
connection.row_overrides["SHOW FRONTENDS"] = [
{
"Name": "fe-1",
"IsMaster": "true",
"Version": "4.0.5",
},
]
connection.row_overrides["SHOW BACKENDS"] = [
{
"BackendId": "1",
"Version": "4.0.5",
},
]
detector = DorisCapabilityDetector( # type: ignore[arg-type]
_ProbeConnectionManager(connection)
)

snapshot = await detector.detect_base(
None,
capability_generation=1,
provider_generation="provider.a",
)
feature = DORIS_FEATURE_MATRIX.evaluate(
domain="doris_catalog",
child_name="list_tables",
versions=snapshot.version_vector,
)
report = DORIS_PATCH_CERTIFICATION_MATRIX.evaluate(snapshot.version_vector)

assert snapshot.version_vector.master_fe.is_parsed is False
assert snapshot.version_vector.master_fe.brand is None
assert snapshot.version_vector.backends[0].is_parsed is False
assert snapshot.version_vector.backends[0].brand is None
assert feature.compatible is False
assert feature.reason_code == "DORIS_VERSION_UNKNOWN"
assert feature.certified is False
assert report.status is VersionCertificationStatus.UNKNOWN
assert report.certified is False
assert report.evidence_ids == ()


@pytest.mark.asyncio
async def test_detector_does_not_treat_commit_substrings_as_brand_tokens() -> None:
configure_version_brands(("db",))
Expand Down
19 changes: 19 additions & 0 deletions test/tools/test_doris_feature_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,25 @@ def test_distribution_evidence_validates_comments_without_brand_registry() -> No
PatchCertificationEvidence.model_validate(payload)


def test_apache_evidence_rejects_distribution_comments_with_registered_alias(
enterprise_brand_alias: str,
) -> None:
evidence = _certification_evidence("4.0.5")
payload = evidence.model_dump(mode="python")
payload["master_fe_version_comment"] = (
f"{enterprise_brand_alias} version 4.0.5"
)
payload["backend_version_comments"] = (
f"{enterprise_brand_alias}-4.0.5-abc1234",
)

with pytest.raises(
ValidationError,
match="Doris core version 4.0.5",
):
PatchCertificationEvidence.model_validate(payload)


def test_patch_evidence_rejects_incomplete_host_or_component_proof() -> None:
evidence = _certification_evidence()
payload = evidence.model_dump(mode="python")
Expand Down
19 changes: 19 additions & 0 deletions test/tools/test_doris_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,25 @@ def test_registered_brand_alias_comments_parse_three_part_version(
assert version.brand_verified is False


@pytest.mark.parametrize(
"comment",
[
"Doris version enterprisedb-4.0.5",
"enterprisedb version doris-4.0.5",
],
)
def test_mismatched_leading_and_version_brands_fail_closed(
comment: str,
enterprise_brand_alias: str,
) -> None:
version = parse_doris_version_comment(comment)

assert version.parse_status is DorisVersionParseStatus.UNKNOWN
assert version.is_parsed is False
assert version.brand is None
assert version.core is None


def test_brand_alias_configuration_replaces_previous_set(
enterprise_brand_alias: str,
) -> None:
Expand Down
Loading