feat(capture_server): auth Bearer + bind localhost + anti-path-traversal

- Token obligatoire (RPA_API_TOKEN) sur /capture et /file-action
- Bind 127.0.0.1 par défaut, 0.0.0.0 exige token (fail-closed)
- /health reste public pour monitoring
- VWB backend injecte le Bearer pour les proxys distants
- hmac.compare_digest pour comparaison temps constant

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-04-14 16:47:45 +02:00
parent 013fe071a2
commit c77844fa9a
3 changed files with 129 additions and 12 deletions

View File

@@ -136,8 +136,18 @@ def capture_windows():
agent_port = int(os.environ.get('RPA_WINDOWS_AGENT_PORT', '5006'))
agent_url = f'http://{agent_host}:{agent_port}/capture'
# Auth : l'agent exige un Bearer token (meme RPA_API_TOKEN que le streaming)
api_token = os.environ.get('RPA_API_TOKEN', '')
headers = {'Authorization': f'Bearer {api_token}'} if api_token else {}
try:
resp = http_client.get(agent_url, timeout=10)
resp = http_client.get(agent_url, headers=headers, timeout=10)
if resp.status_code == 401:
return jsonify({
'error': 'Agent Windows : authentification refusee',
'hint': 'Verifiez que RPA_API_TOKEN est defini et identique '
'cote backend VWB et cote agent Windows.',
}), 401
if resp.ok:
return jsonify(resp.json())
return jsonify({

View File

@@ -359,7 +359,7 @@ def _execute_db_foreach(
# 3. Pour chaque ligne, injecter et exécuter
iteration_results = []
model = executor_kwargs.get("model", "qwen3-vl:8b")
model = executor_kwargs.get("model", os.environ.get("RPA_VLM_MODEL", os.environ.get("VLM_MODEL", "gemma4:e4b")))
ollama_endpoint = executor_kwargs.get("ollama_endpoint", "http://localhost:11434")
timeout = executor_kwargs.get("timeout", 300)
@@ -514,7 +514,7 @@ def execute_dag(workflow_id: str):
# Paramètres optionnels
timeout = data.get("timeout", 300)
model = data.get("model", "qwen3-vl:8b")
model = data.get("model", os.environ.get("RPA_VLM_MODEL", os.environ.get("VLM_MODEL", "gemma4:e4b")))
ollama_endpoint = data.get("ollama_endpoint", "http://localhost:11434")
executor_kwargs = {
@@ -1000,21 +1000,34 @@ def execute_windows():
file_actions = [a for a in data['actions'] if a.get('type', '') in _FILE_ACTION_TYPES]
if file_actions:
# Exécuter les actions fichiers via l'agent Windows
# Auth : Bearer token obligatoire (capture_server.py exige RPA_API_TOKEN)
_agent_host = os.environ.get('RPA_WINDOWS_AGENT_HOST', '192.168.1.11')
_agent_port = int(os.environ.get('RPA_WINDOWS_AGENT_PORT', '5006'))
_agent_url = f'http://{_agent_host}:{_agent_port}/file-action'
_api_token = os.environ.get('RPA_API_TOKEN', '')
_file_headers = {'Authorization': f'Bearer {_api_token}'} if _api_token else {}
file_results = []
for fa in file_actions:
try:
fa_resp = req.post(
'http://192.168.1.11:5006/file-action',
_agent_url,
json={
'action': fa['type'],
'params': fa.get('parameters', {}),
},
headers=_file_headers,
timeout=30,
)
file_results.append(fa_resp.json())
if fa_resp.status_code == 401:
file_results.append({
'error': "Agent Windows : auth refusee (verifier RPA_API_TOKEN)",
})
else:
file_results.append(fa_resp.json())
except req.ConnectionError:
file_results.append({
'error': "Agent Windows (port 5006) non disponible pour l'action fichier"
'error': f"Agent Windows ({_agent_host}:{_agent_port}) non disponible pour l'action fichier"
})
except Exception as e:
file_results.append({'error': str(e)})