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.
LibreOffice headless conversion on macOS can SIGABRT ("Trace/BPT trap")
in some environments — see upstream bug 169711. The current harness
already isolates the user profile via -env:UserInstallation but is
still vulnerable to stale .~lock files, the start center, and crashed-
session restore prompts. When the direct soffice invocation aborts,
the harness produces a generic "exit -6: <stderr>" error that doesn't
tell the user what to do next.
Changes:
- Always pass --nodefault, --norestore, --nolockcheck alongside
--headless --nologo --nofirststartwizard. Factored into a shared
tuple so both code paths stay in sync.
- On macOS, when the direct soffice run returns a signal or stderr
contains an abort marker, retry once through LaunchServices via
`open -W -n -a <LibreOffice.app bundle> --args …`. This drives
the conversion through the normal macOS app activation pathway
instead of a raw fork/exec, which sidesteps several of the
documented abort scenarios.
- If both paths fail on macOS, raise a clear, actionable error that
links to the upstream bug and includes copy-pasteable manual
workarounds (direct soffice with an isolated profile, and the
LaunchServices form).
- Add 8 unit tests covering: conservative flag set, bundle path
resolution, abort detection (signal + stderr text), macOS retry
invocation, no retry on Linux, and the helpful double-failure
error message.
- Bump cli-anything-libreoffice 1.0.0 → 1.0.1.
This follows the mitigation plan that @yuh-yang outlined in the issue
thread on 2026-04-21 (isolated profile, conservative flags, macOS-
specific error, LaunchServices fallback). It does not — and per
upstream cannot — guarantee a fix for every macOS + LibreOffice
build combination, but it covers the documented mitigations and
fails loudly with a workaround when it can't.
Fixes#221
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace str.split() with shlex.split() in the REPL loop of 9 harnesses
(Blender, Audacity, Inkscape, KDenlive, LibreOffice, OBS Studio, Ollama,
Zoom, ComfyUI) to correctly handle quoted arguments containing spaces.
The GIMP harness already uses this pattern. A ValueError fallback to
str.split() is included for malformed quotes.
FixesHKUDS/CLI-Anything#127
Closes#51
The root cause: all 10 harnesses used bare `open("w") + json.dump()` for
session saves. `open("w")` truncates the file before any lock can be
acquired, so concurrent writes silently corrupt or lose data.
Fix: add `_locked_save_json()` to each session.py that opens with "r+"
(no truncation), acquires fcntl.LOCK_EX, then truncates inside the lock.
Falls back gracefully on Windows where fcntl is unavailable.
Also:
- Update HARNESS.md with session file locking guidance (per maintainer
request on PR #52)
- Add concurrent write tests (4 threads × 50 writes) to verify the fix
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fill in the previously empty command tables with actual commands
extracted from each CLI's Click decorators. All 11 harnesses now
have complete command documentation in their SKILL.md files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move skills/SKILL.md from the harness root into
cli_anything/<software>/skills/SKILL.md so it is installed alongside the
package via pip. Add package_data to all 11 setup.py files. Make
ReplSkin auto-detect the skill file relative to its own __file__
location, removing the need for an explicit skill_path argument.
Update HARNESS.md, cli-anything.md, plugin README, and main README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standardize skill file naming from <software>_SKILL.md to SKILL.md across
all 11 harnesses. Update skill_generator.py, HARNESS.md, repl_skin.py,
cli-anything.md, and plugin README.md to reference the new path. Add
SKILL.md generation section to main README.md and mark roadmap item done.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Changes
### 1. Display skill path in CLI header (repl_skin.py)
- Added `skill_path` parameter to ReplSkin class
- Banner now displays skill path when provided: `◇ Skill: skills/<software>_SKILL.md`
- This enables AI agents to discover where to read the skill documentation
### 2. Generate SKILL.md files for all 11 existing CLI harnesses
- anygen/agent-harness/skills/anygen_SKILL.md
- audacity/agent-harness/skills/audacity_SKILL.md
- blender/agent-harness/skills/blender_SKILL.md
- drawio/agent-harness/skills/drawio_SKILL.md
- gimp/agent-harness/skills/gimp_SKILL.md
- inkscape/agent-harness/skills/inkscape_SKILL.md
- kdenlive/agent-harness/skills/kdenlive_SKILL.md
- libreoffice/agent-harness/skills/libreoffice_SKILL.md
- obs-studio/agent-harness/skills/obs_studio_SKILL.md
- shotcut/agent-harness/skills/shotcut_SKILL.md
- zoom/agent-harness/skills/zoom_SKILL.md
### 3. Update HARNESS.md with skill path guidance
- Updated Phase 3 (REPL section) to show skill_path parameter usage
- Added "Display Skill Path in CLI Banner" section in Phase 6.5
### 4. Fix skill_generator.py syntax error
- Fixed list comprehension indentation on line 80-81
Addresses maintainer feedback from PR #47 comment:
- "displaying the skill path on the cli header so that agents can know where to read it"
- "generate skill.md files for all existing CLIs we have in the repo"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixed 8 setup.py files that had placeholder URLs:
- Changed 'yourusername' to 'HKUDS'
- Changed repo name to 'CLI-Anything'
This fixes broken metadata when packages are published to PyPI.
find_libreoffice() previously only searched PATH, which misses the
default install location on Windows (Program Files\LibreOffice\program)
and macOS (/Applications/LibreOffice.app). Added platform-aware
fallback paths so the backend works out of the box after a standard
install.
get_version() called `soffice --version` without --headless, which
opens a GUI splash on Windows and hangs indefinitely. Now uses
`--headless --version` and gracefully falls back on timeout.
Tested on Windows with LibreOffice 26.2.1:
- 89 unit tests PASS
- 69 E2E tests PASS (including 13 real LibreOffice backend tests)
- PDF, DOCX, XLSX, PPTX, CSV all verified via magic bytes / OOXML
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>