chore(deps): upgrade PyYAML to address known security vulnerability
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 39s
CI / helm (pull_request) Successful in 48s
CI / build (pull_request) Successful in 1m1s
CI / benchmark-regression (pull_request) Failing after 1m15s
CI / lint (pull_request) Failing after 1m20s
CI / quality (pull_request) Successful in 1m21s
CI / typecheck (pull_request) Successful in 1m40s
CI / security (pull_request) Successful in 1m47s
CI / integration_tests (pull_request) Successful in 3m12s
CI / unit_tests (pull_request) Failing after 3m48s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 4m28s
CI / status-check (pull_request) Failing after 4s
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 39s
CI / helm (pull_request) Successful in 48s
CI / build (pull_request) Successful in 1m1s
CI / benchmark-regression (pull_request) Failing after 1m15s
CI / lint (pull_request) Failing after 1m20s
CI / quality (pull_request) Successful in 1m21s
CI / typecheck (pull_request) Successful in 1m40s
CI / security (pull_request) Successful in 1m47s
CI / integration_tests (pull_request) Successful in 3m12s
CI / unit_tests (pull_request) Failing after 3m48s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 4m28s
CI / status-check (pull_request) Failing after 4s
- Added `pyyaml>=6.0.3` dependency constraint to pyproject.toml after aiohttp to prevent installation of vulnerable older versions with known YAML parsing security issues. - Updated uv.lock package dependencies and requires-dist to include pyyaml entry with specifier '>=6.0.3'. - Added changelog Security section entry under [Unreleased] documenting the upgrade for issue #9055. - Updated CONTRIBUTORS.md with contribution note (PR #9244). - Added BDD test scenario verifying PyYAML security dependency constraint. ISSUES CLOSED: #9055
This commit is contained in:
@@ -96,6 +96,11 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
- **PyYAML upgraded to >=6.0.3 to address known security vulnerability** (#9055):
|
||||||
|
Added an explicit `pyyaml>=6.0.3` dependency constraint to `pyproject.toml` to
|
||||||
|
prevent installation of vulnerable older versions with known YAML parsing security
|
||||||
|
issues.
|
||||||
|
|
||||||
- **aiohttp upgraded to >=3.13.4 to remediate CVE-2026-34513 and CVE-2026-34515** (#1549, #1544):
|
- **aiohttp upgraded to >=3.13.4 to remediate CVE-2026-34513 and CVE-2026-34515** (#1549, #1544):
|
||||||
Added an explicit `aiohttp>=3.13.4` dependency constraint to `pyproject.toml` to remediate
|
Added an explicit `aiohttp>=3.13.4` dependency constraint to `pyproject.toml` to remediate
|
||||||
two high-severity open redirect vulnerabilities. Both CVEs affect the CleverAgents platform's
|
two high-severity open redirect vulnerabilities. Both CVEs affect the CleverAgents platform's
|
||||||
|
|||||||
+3
-1
@@ -33,4 +33,6 @@ Below are some of the specific details of various contributions.
|
|||||||
* HAL 9000 has contributed the LLMTraceRepository data-integrity fix (PR #8185 / issue #7505): replaced the unconditional `session.commit()` in `LLMTraceRepository.save()` with a dual-path implementation that respects the UnitOfWork pattern — flushing only when an external session is provided, and flushing + committing + closing when operating standalone. This eliminates premature transaction commits, loss of rollback capability, and a docstring/implementation mismatch.
|
* HAL 9000 has contributed the LLMTraceRepository data-integrity fix (PR #8185 / issue #7505): replaced the unconditional `session.commit()` in `LLMTraceRepository.save()` with a dual-path implementation that respects the UnitOfWork pattern — flushing only when an external session is provided, and flushing + committing + closing when operating standalone. This eliminates premature transaction commits, loss of rollback capability, and a docstring/implementation mismatch.
|
||||||
* HAL 9000 has contributed the ACMS Index Data Model and File Traversal Engine (PR #9664 / issue #9579): foundational data structures for indexed context entries with hot/warm/cold/archive storage tier classification, tag system, and a timeout-safe chunked file traversal engine for large projects with 10,000+ files.
|
* HAL 9000 has contributed the ACMS Index Data Model and File Traversal Engine (PR #9664 / issue #9579): foundational data structures for indexed context entries with hot/warm/cold/archive storage tier classification, tag system, and a timeout-safe chunked file traversal engine for large projects with 10,000+ files.
|
||||||
|
|
||||||
* HAL 9000 has contributed the error-suppression removal fix (PR #9247 / issue #9060): removed both `try...except Exception:` blocks in `register_registry_agents()` that silently suppressed errors from `actor_registry.list_actors()` and the route bridge refresh, enabling exceptions to propagate per CONTRIBUTING.md fail-fast policy. Added three Behave scenarios verifying RuntimeError, AttributeError, and TypeError propagation.
|
* HAL 9000 has contributed the error-suppression removal fix (PR #9247 / issue #9060): removed both `try...except Exception:` blocks in `register_registry_agents()` that silently suppressed errors from `actor_registry.list_actors()` and the route bridge refresh, enabling exceptions to propagate per CONTRIBUTING.md fail-fast policy. Added three Behave scenarios verifying RuntimeError, AttributeError, and TypeError propagation.
|
||||||
|
|
||||||
|
* HAL 9000 has contributed the PyYAML security upgrade (PR #9244 / issue #9055): added `pyyaml>=6.0.3` dependency constraint to address known YAML parsing vulnerabilities.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
@tdd_issue @tdd_issue_9055
|
||||||
|
Feature: PyYAML is a declared project dependency with minimum secure version
|
||||||
|
As a CleverAgents developer
|
||||||
|
I want the PyYAML dependency to be listed in pyproject.toml with a minimum secure version
|
||||||
|
So that vulnerable older versions of PyYAML cannot be installed
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the pyproject.toml file exists at "pyproject.toml"
|
||||||
|
|
||||||
|
@tdd_issue @tdd_issue_9055
|
||||||
|
Scenario: PyYAML is listed in project dependencies with minimum version constraint
|
||||||
|
When I read the project dependencies from pyproject.toml
|
||||||
|
Then the dependency list should include a package matching "pyyaml>="
|
||||||
|
|
||||||
|
@tdd_issue @tdd_issue_9055
|
||||||
|
Scenario: PyYAML minimum version is >=6.0.3
|
||||||
|
When I read the PyYAML dependency specification from pyproject.toml
|
||||||
|
Then the minimum version should be at least "6.0.3"
|
||||||
|
|
||||||
|
@tdd_issue @tdd_issue_9055
|
||||||
|
Scenario: yaml module is importable as a project dependency
|
||||||
|
When I attempt to import the "yaml" module
|
||||||
|
Then the import should succeed without errors
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
"""Step definitions for PyYAML security dependency verification (#9055)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from behave import given, then, when
|
||||||
|
from behave.runner import Context
|
||||||
|
|
||||||
|
|
||||||
|
@given('the pyproject.toml file exists at "{path}"')
|
||||||
|
def step_pyproject_exists(context: Context, path: str) -> None:
|
||||||
|
"""Verify pyproject.toml exists at the given path."""
|
||||||
|
pyproject_path = Path(path)
|
||||||
|
assert pyproject_path.exists(), f"pyproject.toml not found at {path}"
|
||||||
|
context.pyproject_path = pyproject_path
|
||||||
|
|
||||||
|
|
||||||
|
@when("I read the project dependencies from pyproject.toml")
|
||||||
|
def step_read_project_dependencies(context: Context) -> None:
|
||||||
|
"""Read the [project.dependencies] list from pyproject.toml."""
|
||||||
|
content = context.pyproject_path.read_bytes()
|
||||||
|
data: dict[str, Any] = _load_toml(content)
|
||||||
|
context.project_dependencies = data.get("project", {}).get("dependencies", [])
|
||||||
|
|
||||||
|
|
||||||
|
@then('the dependency list should include a package matching "{pattern}"')
|
||||||
|
def step_dependency_matches_pattern(context: Context, pattern: str) -> None:
|
||||||
|
"""Assert that a dependency in the project dependencies matches the given regex pattern."""
|
||||||
|
deps: list[str] = context.project_dependencies
|
||||||
|
found = any(re.search(pattern, dep) for dep in deps)
|
||||||
|
assert found, (
|
||||||
|
f"No dependency matching '{pattern}' found in "
|
||||||
|
f"[project.dependencies]. Current deps: {deps}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@when("I read the PyYAML dependency specification from pyproject.toml")
|
||||||
|
def step_read_pyyaml_dependency(context: Context) -> None:
|
||||||
|
"""Read and parse the PyYAML dependency from pyproject.toml."""
|
||||||
|
content = context.pyproject_path.read_bytes()
|
||||||
|
data: dict[str, Any] = _load_toml(content)
|
||||||
|
deps: list[str] = data.get("project", {}).get("dependencies", [])
|
||||||
|
|
||||||
|
# Find the pyyaml dependency entry
|
||||||
|
pyyaml_dep = None
|
||||||
|
for dep in deps:
|
||||||
|
if dep.strip().startswith("pyyaml"):
|
||||||
|
pyyaml_dep = dep.strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
assert pyyaml_dep is not None, (
|
||||||
|
f"PyYAML dependency not found in [project.dependencies]. Current deps: {deps}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Parse the minimum version from the constraint
|
||||||
|
# Examples: "pyyaml>=6.0.3", "pyyaml>=6.0", "pyyaml==6.0.2"
|
||||||
|
match = re.search(r"(?:>=|==|~>)([\d]+(?:\.[\d]+)*(?:\.\w+)*)", pyyaml_dep)
|
||||||
|
assert match is not None, f"Could not parse version from PyYAML dependency: {pyyaml_dep}"
|
||||||
|
|
||||||
|
context.pyyaml_version_spec = pyyaml_dep
|
||||||
|
context.pyyaml_min_version = match.group(1)
|
||||||
|
|
||||||
|
|
||||||
|
@then("the minimum version should be at least \"{expected}\"")
|
||||||
|
def step_pyyaml_minimum_version(context: Context, expected: str) -> None:
|
||||||
|
"""Assert that the PyYAML minimum version is >= expected version."""
|
||||||
|
min_version = context.pyyaml_min_version
|
||||||
|
assert min_version == expected, (
|
||||||
|
f"PyYAML minimum version {min_version} does not match expected {expected}. "
|
||||||
|
f"Dependency spec: {context.pyyaml_version_spec}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@when('I attempt to import the "{module_name}" module')
|
||||||
|
def step_attempt_import(context: Context, module_name: str) -> None:
|
||||||
|
"""Attempt to import the given module and record the result."""
|
||||||
|
try:
|
||||||
|
context.imported_module = importlib.import_module(module_name)
|
||||||
|
context.import_error = None
|
||||||
|
except ImportError as exc:
|
||||||
|
context.imported_module = None
|
||||||
|
context.import_error = exc
|
||||||
|
|
||||||
|
|
||||||
|
@then("the import should succeed without errors")
|
||||||
|
def step_import_succeeded(context: Context) -> None:
|
||||||
|
"""Assert that the previous import attempt succeeded."""
|
||||||
|
assert context.import_error is None, f"Import failed with: {context.import_error}"
|
||||||
|
assert context.imported_module is not None, "Module was not imported"
|
||||||
|
|
||||||
|
|
||||||
|
@then('the version constraint in uv.lock should include specifier "{specifier}"')
|
||||||
|
def step_uv_lock_specifier(context: Context, specifier: str) -> None:
|
||||||
|
"""Assert that pyyaml appears in both package.dependencies and requires-dist in uv.lock."""
|
||||||
|
lock_path = context.pyproject_path.parent / "uv.lock"
|
||||||
|
content = lock_path.read_bytes().decode("utf-8")
|
||||||
|
|
||||||
|
# Check that pyyaml is in the cleveragents package dependencies section
|
||||||
|
assert (
|
||||||
|
'[[package]]\nname = "cleveragents"' in content
|
||||||
|
), "cleveragents package section not found in uv.lock"
|
||||||
|
|
||||||
|
# Verify pyyaml entry exists in file after cleveragents section
|
||||||
|
cleveragents_match = re.search(
|
||||||
|
r'\[\[package\]\]\s*name = "cleveragents"(.*?)^\[\[package\]\]',
|
||||||
|
content,
|
||||||
|
re.DOTALL | re.MULTILINE,
|
||||||
|
)
|
||||||
|
assert cleveragents_match, "Could not find cleveragents package section"
|
||||||
|
|
||||||
|
cleveragents_block = cleveragents_match.group(1)
|
||||||
|
|
||||||
|
assert '{ name = "pyyaml" }' in cleveragents_block, (
|
||||||
|
f"pyyaml not found in cleveragents package dependencies in uv.lock"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_toml(content: bytes) -> dict[str, Any]:
|
||||||
|
"""Load a TOML file from bytes. Uses tomllib if available, else fallback."""
|
||||||
|
try:
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
return tomllib.loads(content.decode("utf-8"))
|
||||||
|
except ImportError:
|
||||||
|
# Fallback for older Python versions without tomllib
|
||||||
|
import tomlkit
|
||||||
|
|
||||||
|
return tomlkit.parse(content.decode("utf-8")).unwrap() # type: ignore[no-any-return]
|
||||||
@@ -47,6 +47,7 @@ dependencies = [
|
|||||||
"tomlkit>=0.13.0", # TOML writing with comment preservation for config CLI
|
"tomlkit>=0.13.0", # TOML writing with comment preservation for config CLI
|
||||||
"tenacity>=8.2.0", # Retry framework for service layer resilience
|
"tenacity>=8.2.0", # Retry framework for service layer resilience
|
||||||
"aiohttp>=3.13.4", # CVE-2026-34515 mitigation: open redirect vulnerability
|
"aiohttp>=3.13.4", # CVE-2026-34515 mitigation: open redirect vulnerability
|
||||||
|
"pyyaml>=6.0.3", # Security: address known YAML parsing vulnerabilities
|
||||||
"a2a-sdk>=0.3.0,<1.0.0", # A2A Python SDK — required transport for local (stdio) and server (HTTP) modes (ADR-047); pinned <1.0.0 (removed legacy A2AClient)
|
"a2a-sdk>=0.3.0,<1.0.0", # A2A Python SDK — required transport for local (stdio) and server (HTTP) modes (ADR-047); pinned <1.0.0 (removed legacy A2AClient)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -446,6 +446,7 @@ dependencies = [
|
|||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "pydantic-settings" },
|
{ name = "pydantic-settings" },
|
||||||
{ name = "python-ulid" },
|
{ name = "python-ulid" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
{ name = "restrictedpython" },
|
{ name = "restrictedpython" },
|
||||||
{ name = "rx" },
|
{ name = "rx" },
|
||||||
{ name = "structlog" },
|
{ name = "structlog" },
|
||||||
@@ -528,6 +529,7 @@ requires-dist = [
|
|||||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
|
||||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" },
|
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" },
|
||||||
{ name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" },
|
{ name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" },
|
||||||
|
{ name = "pyyaml", specifier = ">=6.0.3" },
|
||||||
{ name = "python-ulid", specifier = ">=2.7.0" },
|
{ name = "python-ulid", specifier = ">=2.7.0" },
|
||||||
{ name = "radon", marker = "extra == 'dev'", specifier = ">=6.0.1" },
|
{ name = "radon", marker = "extra == 'dev'", specifier = ">=6.0.1" },
|
||||||
{ name = "restrictedpython", specifier = ">=7.0" },
|
{ name = "restrictedpython", specifier = ">=7.0" },
|
||||||
|
|||||||
Reference in New Issue
Block a user