Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/ai-evaluation-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ jobs:
--threshold ${{ env.EVALUATION_THRESHOLD }}

- name: Upload evaluation artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: always()
with:
name: evaluation-results-${{ matrix.model }}
Expand Down
26 changes: 19 additions & 7 deletions .github/workflows/production-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install ruff mypy bandit radon pytest pytest-cov
pip install -r requirements-dev.txt
pip install bandit radon
pip install -e packages/vertice-core

- name: Install Node.js dependencies
run: |
cd apps/web-console
npm ci

# Quality Checks
Expand Down Expand Up @@ -124,10 +127,13 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov pytest-asyncio
pip install -r requirements-dev.txt
pip install -e packages/vertice-core

- name: Install Node.js dependencies
run: npm ci
run: |
cd apps/web-console
npm ci

# Database setup
- name: Setup test database
Expand Down Expand Up @@ -186,7 +192,9 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
pip install psutil
pip install -e packages/vertice-core

- name: Run load tests
run: python tests/load/test_load_performance.py
Expand Down Expand Up @@ -227,7 +235,7 @@ jobs:
run: audit-ci --config audit-ci.json || true

- name: Upload security reports
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: security-scan-reports
path: |
Expand Down Expand Up @@ -268,10 +276,14 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
pip install pyinstaller
pip install -e packages/vertice-core

- name: Install Node.js dependencies
run: npm ci
run: |
cd apps/web-console
npm ci

# Build backend
- name: Build Python application
Expand Down Expand Up @@ -315,7 +327,7 @@ jobs:
uses: actions/checkout@v4

- name: Download build artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: build-artifacts

Expand Down Expand Up @@ -357,7 +369,7 @@ jobs:
uses: actions/checkout@v4

- name: Download build artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: build-artifacts

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
- name: Check cyclomatic complexity
run: |
echo "Checking cyclomatic complexity..."
radon cc packages/vertice-core/src/vertice_core -a -nc --fail D
radon cc packages/vertice-core/src/vertice_core -a -nc --min D

# Maintainability Index Check - Min A (20+)
- name: Check maintainability index
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/vertice-mcp-cicd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ jobs:
- name: 🔍 Basic Code Quality Check
run: |
echo "Running basic validation..."
python -c "import ast; ast.parse(open('vertice_cli/__init__.py').read()); print('✅ Python syntax OK')"
python -c "import ast; ast.parse(open('vertice_tui/__init__.py').read()); print('✅ TUI syntax OK')"
python -c "import ast; ast.parse(open('packages/vertice-core/src/vertice_core/cli/__init__.py').read()); print('✅ Python syntax OK')"
python -c "import ast; ast.parse(open('packages/vertice-core/src/vertice_core/tui/__init__.py').read()); print('✅ TUI syntax OK')"
echo "✅ Basic validation passed"

build:
Expand Down
21 changes: 21 additions & 0 deletions apps/agent-gateway/app/jules/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

from __future__ import annotations

import hashlib
import hmac
import json
import logging
from typing import Any, Dict, List, Optional
Expand Down Expand Up @@ -323,6 +325,25 @@ async def handle_github_webhook(request: Request) -> Dict[str, Any]:
# Get event type
event_type = request.headers.get("X-GitHub-Event", "")

# Verify signature if secret is configured
webhook_secret = service.config.github_webhook_secret
if webhook_secret:
signature = request.headers.get("X-Hub-Signature-256", "")
if not signature:
raise HTTPException(status_code=401, detail="Missing webhook signature")

body = await request.body()
expected_signature = "sha256=" + hmac.new(
webhook_secret.encode("utf-8"),
body,
hashlib.sha256
).hexdigest()

if not hmac.compare_digest(signature, expected_signature):
raise HTTPException(status_code=401, detail="Invalid webhook signature")
else:
logger.warning("GitHub webhook secret not configured - skipping verification")
Comment on lines +330 to +345

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The GitHub webhook signature verification logic currently 'fails open' if github_webhook_secret is not configured, allowing spoofing attacks. This poses a significant security risk. Additionally, the request body stream can only be read once; reading it twice (once for verification and once for JSON parsing) will cause the webhook handler to fail when a secret is configured. The suggested code addresses the 'fail open' vulnerability by enforcing verification and ensures the request body is read only once for signature verification.

Suggested change
if webhook_secret:
signature = request.headers.get("X-Hub-Signature-256", "")
if not signature:
raise HTTPException(status_code=401, detail="Missing webhook signature")
body = await request.body()
expected_signature = "sha256=" + hmac.new(
webhook_secret.encode("utf-8"),
body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
raise HTTPException(status_code=401, detail="Invalid webhook signature")
else:
logger.warning("GitHub webhook secret not configured - skipping verification")
if not webhook_secret:
logger.error("GitHub webhook secret not configured - rejecting request")
raise HTTPException(status_code=500, detail="Webhook verification not configured")
signature = request.headers.get("X-Hub-Signature-256", "")
if not signature:
raise HTTPException(status_code=401, detail="Missing webhook signature")
body = await request.body()
expected_signature = "sha256=" + hmac.new(
webhook_secret.encode("utf-8"),
body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
raise HTTPException(status_code=401, detail="Invalid webhook signature")


# Parse payload
try:
payload = await request.json()
Expand Down
2 changes: 2 additions & 0 deletions apps/agent-gateway/app/jules/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class JulesConfig:
github_private_key: str = ""
github_installation_id: str = ""
github_repo: str = ""
github_webhook_secret: str = ""

# Jules API (if using hosted Jules)
jules_api_endpoint: str = ""
Expand Down Expand Up @@ -58,6 +59,7 @@ def from_env(cls) -> "JulesConfig":
github_private_key=os.getenv("JULES_GITHUB_PRIVATE_KEY", ""),
github_installation_id=os.getenv("JULES_GITHUB_INSTALLATION_ID", ""),
github_repo=os.getenv("JULES_GITHUB_REPO", "vertice-ai/vertice-code"),
github_webhook_secret=os.getenv("JULES_GITHUB_WEBHOOK_SECRET", ""),
jules_api_endpoint=os.getenv("JULES_API_ENDPOINT", ""),
jules_api_key=os.getenv("JULES_API_KEY", ""),
enable_daily_scan=os.getenv("JULES_ENABLE_DAILY_SCAN", "true").lower() == "true",
Expand Down
6 changes: 3 additions & 3 deletions packages/vertice-core/src/vertice_core/adk/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ def get_schemas(self) -> List[Dict[str, Any]]:

# Basic type mapping
ptype = "string"
if param.annotation == int:
if param.annotation is int:
ptype = "integer"
elif param.annotation == bool:
elif param.annotation is bool:
ptype = "boolean"
elif param.annotation == float:
elif param.annotation is float:
ptype = "number"

properties[param_name] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import logging
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Set, Tuple
from typing import Any, Dict, List, Optional, Set
from enum import Enum

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -237,12 +237,14 @@ def _detect_agent_type(self, description: str) -> str:
# Mapeamento de keywords para agentes
# Ordem importa: mais específicos primeiro
keyword_map = {
"tester": ["automated test", "e2e testing", "end-to-end test", "write tests", "test suite"],
"tester": [
"automated test", "e2e testing", "end-to-end test", "write tests", "test suite",
"test", "qa", "validate", "verify"
],
"security": ["security", "auth", "authentication", "encrypt", "vulnerability", "penetration"],
"devops": ["deploy", "ci/cd", "pipeline", "infrastructure", "monitor", "docker", "kubernetes"],
"architect": ["design", "architecture", "system design", "scalability", "integration"],
"reviewer": ["code review", "pr review", "audit", "assessment"],
"tester": ["test", "qa", "validate", "verify"], # Depois dos mais específicos
}

for agent, keywords in keyword_map.items():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from ...providers.smart_router_v2 import (
SmartRouterV2,
TaskType,
ProviderType,
)

logger = logging.getLogger(__name__)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def _estimate_from_logits(

# Convert to probabilities with softmax
max_logit = max(logits)
exp_logits = [math.exp(l - max_logit) for l in logits]
exp_logits = [math.exp(logit - max_logit) for logit in logits]
sum_exp = sum(exp_logits)
probs = [e / sum_exp for e in exp_logits]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@
from vertice_core.core.logging_setup import setup_logging # noqa: E402

# Tools
from vertice_core.tools.bash_ops import BashCommandTool
from vertice_core.tools.file_ops import (
ReadFileTool,
WriteFileTool,
EditFileTool,
)
from vertice_core.tools.git_ops import (
GitStatusTool,
GitDiffTool,
)

# Agents
from vertice_core.agents.bundle import (
Expand Down
2 changes: 1 addition & 1 deletion packages/vertice-core/src/vertice_core/code/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
stacklevel=2,
)

from .validator import *
from .validator import * # noqa: F403
7 changes: 6 additions & 1 deletion packages/vertice-core/src/vertice_core/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@

from __future__ import annotations

import sys
from typing import (
Any,
Callable,
Coroutine,
Dict,
List,
Literal,
NotRequired,
Optional,
Protocol,
TypeAlias,
Expand All @@ -30,6 +30,11 @@
Union,
runtime_checkable,
)

if sys.version_info >= (3, 11):
from typing import NotRequired
else:
from typing_extensions import NotRequired
from pathlib import Path
from datetime import datetime
from dataclasses import dataclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@ async def close(self):
if self._ws:
try:
await self._ws.close()
except:
except Exception:
pass
self._ws = None
57 changes: 57 additions & 0 deletions packages/vertice-core/src/vertice_core/shell_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,63 @@ def _get_semantic_indexer():

from .core.token_tracker import TokenTracker

# TUI Styles & Components
from .tui.styles import get_rich_theme
from .tui.input_enhanced import InputContext, EnhancedInputSession
from .tui.history import CommandHistory, SessionReplay, HistoryEntry
from .tui.components.workflow.visualizer import WorkflowVisualizer, ExecutionTimeline
from .tui.components.palette import create_default_palette
from .tui.animations import Animator, AnimationConfig, StateTransition
from .tui.components.dashboard import Dashboard

# Tools
from .tools.file_ops import (
ReadFileTool,
WriteFileTool,
EditFileTool,
ReadMultipleFilesTool,
ListDirectoryTool,
DeleteFileTool,
MoveFileTool,
CopyFileTool,
CreateDirectoryTool,
SearchFilesTool,
GetDirectoryTreeTool,
)
from .tools.file_mgmt import InsertLinesTool
from .tools.bash_ops import BashCommandTool
from .tools.git_ops import GitStatusTool, GitDiffTool
from .tools.context import GetContextTool
from .tools.session import SaveSessionTool, RestoreBackupTool
from .tools.terminal import (
CdTool,
LsTool,
PwdTool,
MkdirTool,
RmTool,
CpTool,
MvTool,
TouchTool,
CatTool,
)
from .tools.noesis_mcp import (
GetNoesisConsciousnessTool,
ActivateNoesisConsciousnessTool,
DeactivateNoesisConsciousnessTool,
QueryNoesisTribunalTool,
ShareNoesisInsightTool,
)
from .tools.distributed_noesis_mcp import (
ActivateDistributedConsciousnessTool,
DeactivateDistributedConsciousnessTool,
GetDistributedConsciousnessStatusTool,
ProposeDistributedCaseTool,
GetDistributedCaseStatusTool,
ShareDistributedInsightTool,
GetCollectiveInsightsTool,
ConnectToDistributedNodeTool,
)

# Phase 8: LSP - Lazy loaded in property
_LSPClient = None

Expand Down
10 changes: 5 additions & 5 deletions packages/vertice-core/src/vertice_core/tools/smart_match.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ def strip_common_indent(text: str) -> Tuple[str, int]:
Tuple of (stripped_text, indent_amount)
"""
lines = text.split("\n")
non_empty_lines = [l for l in lines if l.strip()]
non_empty_lines = [line for line in lines if line.strip()]

if not non_empty_lines:
return text, 0

# Find minimum indentation
min_indent = min(len(l) - len(l.lstrip()) for l in non_empty_lines)
min_indent = min(len(line) - len(line.lstrip()) for line in non_empty_lines)

# Strip that amount from all lines
stripped_lines = []
Expand Down Expand Up @@ -125,7 +125,7 @@ def find_with_any_indent(search: str, content: str) -> Optional[Tuple[int, int,

if match:
# Calculate position in original content
start = sum(len(l) + 1 for l in content_lines[:i])
start = sum(len(line) + 1 for line in content_lines[:i])
matched_text = "\n".join(matched_lines)
end = start + len(matched_text)
return (start, end, matched_text)
Expand Down Expand Up @@ -162,7 +162,7 @@ def find_fuzzy_lines(

if ratio > best_ratio:
best_ratio = ratio
start = sum(len(l) + 1 for l in content_lines[:i])
start = sum(len(line) + 1 for line in content_lines[:i])
matched_text = "\n".join(window)
end = start + len(matched_text)
best_match = (start, end, matched_text, ratio)
Expand Down Expand Up @@ -245,7 +245,7 @@ def smart_find(search: str, content: str, strict: bool = False) -> MatchResult:
original_lines = content.split("\n")

if line_num < len(original_lines):
start = sum(len(l) + 1 for l in original_lines[:line_num])
start = sum(len(line) + 1 for line in original_lines[:line_num])
search_line_count = norm_search.count("\n") + 1
matched_text = "\n".join(original_lines[line_num : line_num + search_line_count])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@

try:
from pygments import lex
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.token import Token
from pygments.lexers import get_lexer_by_name
from pygments.util import ClassNotFound

PYGMENTS_AVAILABLE = True
Expand Down
Loading
Loading