Commit Graph

13 Commits

Author SHA1 Message Date
aeonframework 1afe545929 fix(security): route untrusted XML parsing through defusedxml
The repo's threat model (SECURITY.md) calls out XML/SVG content as a key
attack surface and lists "ElementTree auto-escaping" as the mitigation.
That covers XML *output* (serialization). XML *input* (parsing) across
the agent backends still goes through stdlib `xml.etree.ElementTree`,
which is vulnerable to entity-expansion DoS ("billion laughs" /
quadratic blowup) — Python 3.7.1 disabled external-entity lookup on ET
by default, but internal-entity expansion is still possible and can
exhaust CPU/RAM from a small file.

Migrate the parse/fromstring call sites in production code paths to
defusedxml.ElementTree (drop-in API). Construction calls (Element,
SubElement, tostring, register_namespace) are left on stdlib ET — those
don't parse untrusted input. Tests are left as-is in this PR.

Detected by Aeon + semgrep p/security-audit + p/owasp-top-ten
(rules: use-defused-xml, use-defused-xml-parse).
Severity: medium (DoS from agent-ingested files; not RCE).
CWE-776 Improper Restriction of Recursive Entity References.
2026-05-19 07:59:52 +00:00
haorui-harry 8cdbafe301 fix(macrocli): address remaining review blockers
- Remove duplicate macro_record implementation in macrocli_cli.py
  (stale second implementation from lines 582-706 that would cause
  duplicate saves and unexpected behavior)
- Update MACROCLI.md: document visual_anchor and gui_agent backends,
  optional dependencies ([visual], [gui_agent]), and OpenAI-compatible
  LLM configuration (MACROCLI_MODEL/API_KEY/BASE_URL)
- Resolve .gitignore conflicts: add sbox and quietshrink directories
  from upstream while preserving macrocli entries (Step 4/5/6 pattern)
2026-05-04 18:21:42 +08:00
haorui-harry 0f301dbf4f refactor(macrocli): replace Gemini with generic OpenAI-compatible LLM backend
- Rename gemini_assist.py → llm_assist.py, rewrite to use OpenAI SDK
- Rewrite parameterize.py's gemini_suggest_parameters → llm_suggest_parameters
  (old name kept as alias for backwards compat)
- Update macrocli_cli.py: all Gemini references → generic LLM, env vars
  MACROCLI_MODEL / MACROCLI_API_KEY / MACROCLI_BASE_URL
- Update recorder.py UI text
- Add .gitignore rules to exclude non-gedit demo files from tracking
2026-05-03 00:41:31 +08:00
haorui-harry fb767afe0a fix(macrocli): address remaining review blockers
- Remove flameshot_annotate and kolourpaint_annotate from manifest
  (only gedit demos are shipped)
- Remove kolourpaint demo YAML and snapshot from tracked files
- Fix gui_agent model defaults: remove hardcoded Gemini model name,
  require user to set MACROCLI_MODEL env var or pass model in params
- Replace stale 'gemini' extra with 'gui_agent' extra (openai SDK)
- Remove google-generativeai dependency (unused)
- Update README: document gui_agent and visual_anchor backends,
  optional extras, and environment variables for model configuration
- Simplify package_data (no PNG assets shipped with gedit-only demos)
2026-05-03 00:25:12 +08:00
haorui-harry 1d5312f9e8 fix(macrocli): address review feedback - gitignore, package_data, manifest, and dependencies
- Add /macrocli/ to root .gitignore allowlist (Step 4/5/6 pattern)
- Include demo macro YAML files and snapshot PNGs in package_data
- Add kolourpaint_annotate to manifest.yaml
- Add openai dependency to extras_require (gui_agent.py imports OpenAI)
2026-04-30 00:48:42 +08:00
haorui-harry 960adb58fa feat(macrocli): add gui_agent instruct_with_refine, drag via xdotool, kolourpaint demo
gui_agent backend:
  - Add instruct_with_refine action: executes drag, captures before/after
    screenshots, sends all three images (before, after, target) to model
    for comparison, model provides corrected coordinates, undo + re-execute
  - Add drag to action space and _execute_action
  - max_tokens=1024 to prevent JSON truncation on drag actions
  - Support instruct and instruct_with_refine actions

visual_anchor:
  - _mouse_drag: use xdotool first (reliable with Qt5/KDE apps like
    KolourPaint), fall back to pynput if xdotool unavailable

macro_definitions:
  - Add kolourpaint_annotate demo macro (hybrid fixed+agent annotation)
  - Add end_state snapshot for annotation grounding
  - Register kolourpaint_annotate in manifest.yaml

64/64 tests passing
2026-04-22 00:48:15 +08:00
haorui-harry 6ecf72d9c9 feat(macrocli): add gui_agent backend and hybrid fixed+agent macro recording
gui_agent backend (backends/gui_agent.py):
  - Executes macro steps via Gemini Vision in a perception-action loop
  - Takes current screenshot + end_state_snapshot + description
  - Asks model for next action (click/type/hotkey/scroll/done)
  - Verifies end state reached by comparing screenshots with model
  - Strict action space: 7 allowed actions, no arbitrary code
  - Loops up to max_steps, returns reached_end_state in output

recorder.py:
  - RecordedStep: add is_agent_step, agent_description,
    agent_end_state_description, agent_end_state_snapshot fields
  - to_step_dict(): emit gui_agent/instruct for agent steps
  - interactive_agent_review(): post-recording step-by-step review,
    mark steps as fixed or agent, capture end-state snapshots
  - save_as_package(): save as <name>/ folder with macro.yaml + snapshots/
  - _capture_end_state_snapshot(): take full-screen snapshot

macro record --agent-review: trigger interactive review after recording
Macro package structure: <name>/macro.yaml + <name>/snapshots/*.png
2026-04-21 20:31:18 +08:00
haorui-harry 4cef98473b feat(macrocli): add interactive and Gemini-assisted macro parameterization
core/parameterize.py (new):
  - interactive_parameterize(): CLI prompt loop, validates snake_case names,
    skips on Enter, detects duplicate names
  - parameterize_yaml_file(): post-hoc parameterization of existing YAML files
  - gemini_suggest_parameters(): sends type_text values to Gemini with strict
    prompt, returns {step_index: param_name} suggestions
  - _valid_param_name(): enforces [a-z][a-z0-9_]* format

core/recorder.py:
  - to_yaml(parameters=None): accept pre-built parameters block
  - save(parameters=None): pass through to to_yaml
  - get_type_steps(): list (index, step) for all non-empty type_text steps
  - apply_parameterization(assignments): replace text values with ${param}
    in-place, auto-infer type (string/integer/float), return parameters block

macrocli_cli.py:
  - macro record --parameterize: interactive loop after recording
  - macro record --auto-parameterize [--api-key]: Gemini suggestions + confirm
  - macro parameterize <yaml_file>: post-hoc parameterization of any YAML
  - macro parameterize --auto [--api-key]: Gemini-assisted post-hoc
2026-04-21 16:50:10 +08:00
haorui-harry c52ce5fb97 docs(macrocli): add manifest.yaml demo entries and full DEMO.md
manifest.yaml: register 4 gedit demo macros

DEMO.md: complete live-tested walkthrough including:
- Xvfb + openbox + x11vnc + noVNC setup (headless Linux server)
- SSH tunnel for browser-based VNC access
- Backend availability check
- Demo A: hand-written macros (gedit_new_window, type_and_save, save_as)
- Demo B: macro record + replay with window-anchor explanation
- Demo C: transform_json marked as unverified
- Troubleshooting: conda run DISPLAY issue, VNC multi-client, timeout
2026-04-21 01:46:20 +08:00
haorui-harry f90b51e3c6 fix(recorder): use xdotool getwindowfocus instead of wmctrl parsing
Replace fragile wmctrl -lG text parsing (hostname column caused
'(/tmp)' bug) with xdotool getwindowfocus getwindowgeometry --shell.
Single subprocess call, no column counting, works regardless of
hostname format or window title content.
2026-04-21 01:46:20 +08:00
haorui-harry 242445f9c0 fix(recorder): window-anchored click_relative, fix space/ctrl bugs
- _get_active_window_at(): query wmctrl -lG to find window under click
  point, return (app_title, bounds); fix split(None,7) to capture full
  title including spaces
- RecordedStep: add window_title, x_pct, y_pct fields
- to_step_dict(): prefer click_relative with window_title anchor over
  click_image (robust to window movement/resize)
- Featureless template detection: skip saving if std < 8.0
- space key treated as printable char, accumulated into type_text
- Modifier key dedup: skip recording hotkey when only modifiers pressed
- Template padding increased 30px -> 60px for better matching
2026-04-21 01:46:20 +08:00
haorui-harry 17b89ac982 fix(macrocli): fixes from live gedit testing on Xvfb
native_api: add start_process action for background GUI app launch
  - uses Popen with start_new_session=True, never waits for exit
  - gedit_new_window now uses start_process instead of run_command

semantic_ui + visual_anchor: pass DISPLAY via _x_env() to all
  subprocess calls (wmctrl, xdotool, xwininfo)
  - fixes breakage when running via conda run which strips DISPLAY

gedit_new_window: use start_process + env.DISPLAY for reliable launch
gedit_save_as: use Ctrl+L + Ctrl+A to clear path before typing,
  remove postcondition (gedit writes async after dialog closes)
CLI: add --macro-file option to macro run (bypass registry)
DEMO.md: fix command examples, add test notes
2026-04-21 01:46:20 +08:00
haorui-harry 9db82326e5 refactor: rename openclaw-skill → macrocli throughout
- Directory: openclaw-skill/ → macrocli/
- Python package: cli_anything.openclaw → cli_anything.macrocli
- PyPI package: cli-anything-openclaw → cli-anything-macrocli
- CLI entry: cli-anything-openclaw → cli-anything-macrocli
- Main CLI file: openclaw_cli.py → macrocli_cli.py
- SOP doc: OPENCLAW.md → MACROCLI.md
- registry.json: name openclaw-macro → macrocli
- Session dir: ~/.openclaw-macro → ~/.macrocli
- skills/cli-anything-openclaw/ → skills/cli-anything-macrocli/

64/64 tests passing after rename
2026-04-21 01:46:20 +08:00