This is an automated email from the ASF dual-hosted git repository.
FreeOnePlus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-mcp-server.git
The following commit(s) were added to refs/heads/master by this push:
new 5daf1de fix: preserve version brand provenance (#218)
5daf1de is described below
commit 5daf1deb26bc0db02c19bf5ca1d070acea4cfab9
Author: Yijia Su <[email protected]>
AuthorDate: Fri Aug 14 00:22:43 2026 +0800
fix: preserve version brand provenance (#218)
---
CHANGELOG.md | 4 +++
doris_mcp_server/tools/capability_detector.py | 4 ++-
doris_mcp_server/tools/doris_feature_matrix.py | 45 +++++++++++-------------
doris_mcp_server/tools/doris_version.py | 15 +++++---
test/tools/test_capability_detector.py | 47 ++++++++++++++++++++++++++
test/tools/test_doris_feature_matrix.py | 19 +++++++++++
test/tools/test_doris_version.py | 19 +++++++++++
7 files changed, 123 insertions(+), 30 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 852778c..6b5363d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/doris_mcp_server/tools/capability_detector.py
b/doris_mcp_server/tools/capability_detector.py
index 88c35be..765b6a3 100644
--- a/doris_mcp_server/tools/capability_detector.py
+++ b/doris_mcp_server/tools/capability_detector.py
@@ -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}")
diff --git a/doris_mcp_server/tools/doris_feature_matrix.py
b/doris_mcp_server/tools/doris_feature_matrix.py
index 9e93ab4..b19659d 100644
--- a/doris_mcp_server/tools/doris_feature_matrix.py
+++ b/doris_mcp_server/tools/doris_feature_matrix.py
@@ -462,29 +462,19 @@ class PatchCertificationEvidence(ContractModel):
*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"),
@@ -990,7 +980,7 @@ 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,
@@ -998,11 +988,16 @@ def _distribution_comment_matches(
) -> 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}
diff --git a/doris_mcp_server/tools/doris_version.py
b/doris_mcp_server/tools/doris_version.py
index fbd192b..866c6e9 100644
--- a/doris_mcp_server/tools/doris_version.py
+++ b/doris_mcp_server/tools/doris_version.py
@@ -47,14 +47,16 @@ _known_brands: tuple[str, ...] = (_DEFAULT_BRAND,)
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}}))?
@@ -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")
@@ -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,
)
diff --git a/test/tools/test_capability_detector.py
b/test/tools/test_capability_detector.py
index f6f9acd..1922c73 100644
--- a/test/tools/test_capability_detector.py
+++ b/test/tools/test_capability_detector.py
@@ -1127,6 +1127,53 @@ async def
test_detector_propagates_comment_brand_to_brandless_components(
assert report.certified is False
[email protected]
+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",))
diff --git a/test/tools/test_doris_feature_matrix.py
b/test/tools/test_doris_feature_matrix.py
index 1f0651c..827cb2e 100644
--- a/test/tools/test_doris_feature_matrix.py
+++ b/test/tools/test_doris_feature_matrix.py
@@ -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")
diff --git a/test/tools/test_doris_version.py b/test/tools/test_doris_version.py
index 41a83d5..8524e9c 100644
--- a/test/tools/test_doris_version.py
+++ b/test/tools/test_doris_version.py
@@ -130,6 +130,25 @@ def
test_registered_brand_alias_comments_parse_three_part_version(
assert version.brand_verified is False
[email protected](
+ "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:
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]