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
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Shell scripts must keep LF endings — a CRLF in the shebang breaks them under
# bash/WSL ("bad interpreter"). Applies to all hooks and shell helpers.
*.sh text eol=lf
*.zsh text eol=lf
*.bash text eol=lf

# Python hooks (e.g. mitmproxy addon) also run under Unix interpreters.
*.py text eol=lf
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ npm run build
npx electron-builder --mac # or --win / --linux
```

> **Windows & WSL:** see [docs/windows-wsl-setup.md](docs/windows-wsl-setup.md)
> for build tools, PowerShell notes, packaging, WSL mirrored networking, logging
> from WSL, and operational privacy / isolating private activity.

## Features

### Recording Engine
Expand Down
172 changes: 172 additions & 0 deletions docs/windows-wsl-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Windows & WSL Setup

Setup, packaging, and WSL integration for running RedLog on Windows — plus how
to keep an operator's private activity out of the engagement record.

---

## 1. Prerequisites

RedLog uses the native module `better-sqlite3`, which must be compiled for
Electron's ABI (`npm run rebuild`). That needs a C/C++ toolchain.

| Requirement | Notes |
|---|---|
| **Node.js 20 or 22 (LTS)** | **Not 24+** — newer Node has no prebuilt `better-sqlite3` binary yet, forcing a source build. |
| **Visual Studio Build Tools** | Install the **"Desktop development with C++"** workload. |
| **Python 3** | Required by `node-gyp` for the native rebuild. |
Comment on lines +13 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant files =="
git ls-files | rg '(^|/)(README\.md|docs/windows-wsl-setup\.md)$' || true

echo
echo "== docs/windows-wsl-setup.md around Node setup =="
if [ -f docs/windows-wsl-setup.md ]; then
  nl -ba docs/windows-wsl-setup.md | sed -n '1,80p'
fi

echo
echo "== README.md around Node/rebuild text =="
if [ -f README.md ]; then
  nl -ba README.md | sed -n '1,80p'
fi

echo
echo "== package files and engine/dependency clues =="
fd '(^|/)(package.json|package-lock.json|pnpm-lock.yaml|yarn.lock)$' -t f -x sh -c 'echo "--- $1"; sed -n "1,220p" "$1" | rg -n "engines|node|better-sqlite3|node-pty|electron|rebuild" -C 2 || true' sh {}

echo
echo "== search Node/exclusion mentions =="
rg -n "Node\.js [0-9]|Node [0-9]+|node [0-9]+|20 or 22|not 24|24|22|20|better-sqlite3|node-pty" -S .

Repository: guan4tou2/REDLOG

Length of output: 303


🌐 Web query:

Node.js current LTS version release date 24 status July 2026

💡 Result:

As of today, July 28, 2026, Node.js 24 (codenamed "Krypton") is in the Active LTS (Long Term Support) phase [1][2][3]. The most recent release for this version line is v24.19.0, which was prepared on July 22, 2026 [4]. Node.js 24 entered the Active LTS phase on October 28, 2025, and is scheduled to remain in this status until it transitions to Maintenance mode on October 20, 2026, with an eventual End-of-Life date of April 30, 2028 [1][2][3]. Additionally, the Node.js project announced security releases for the 24.x release line scheduled for, or shortly after, July 27, 2026, to address identified vulnerabilities [5].

Citations:


🌐 Web query:

better-sqlite3 Node.js 24 prebuilt bindings npm package

💡 Result:

As of July 28, 2026, better-sqlite3 supports Node.js 24 [1][2]. Prebuilt binaries for Node.js 24 were introduced in version 12.1.0 of the package [3][2]. If you are encountering installation issues or runtime errors indicating a version mismatch (such as being compiled against an older NODE_MODULE_VERSION), ensure your project is using better-sqlite3 version 12.1.0 or higher [2]. You can update your dependency in package.json to at least 12.1.0 to resolve this [2]: "better-sqlite3": "^12.1.0" If you are on an older version of the library (e.g., 11.x), it will not have the necessary prebuilt binaries for Node.js 24, which can lead to installation failures or attempts to compile from source [2]. Additionally, recent updates to better-sqlite3 have refactored how binaries are handled, moving away from prebuild-install and towards including prebuilt binaries directly within the package, with automatic selection at runtime [4]. If you still encounter issues: 1. Ensure you have updated to the latest version of better-sqlite3 [2]. 2. If compilation from source is required, ensure your environment has the necessary build tools (like python, make, and a C++ compiler) installed, as prebuilt binaries might not be available for every niche platform or architecture [4]. 3. For specific errors, check if you have cached older dependencies; running npm cache clean or deleting node_modules and package-lock.json before reinstalling is recommended [5].

Citations:


🌐 Web query:

node-pty Electron Node 24 build native module

💡 Result:

To use node-pty in an Electron application with Node 24, you must recompile the native module against the specific version of Electron (and its bundled Node.js ABI) rather than your system's Node.js version [1][2][3]. Failing to do so causes ABI mismatch errors, such as posix_spawnp failed or ModuleNotFoundError [1][4][5]. The recommended approach is to use the @electron/rebuild package [6][2][3]. Standard Workflow for Rebuilding 1. Install @electron/rebuild as a development dependency: npm install --save-dev @electron/rebuild 2. After installing your dependencies (which may install prebuilt binaries for your host system), trigger a rebuild specifically for your Electron version: npx electron-rebuild -f -w node-pty This command forces (-f) a rebuild of the specified module (-w) against the Electron version detected in your project [1][6]. Important Considerations - Automation: Ensure this step is part of your build process. If you use Electron Forge, it typically handles this automatically [2][3]. If using Electron Builder or other bundlers, you may need to add it to your postinstall script or as a specific build hook to ensure it runs during packaging [1][4][7]. - ABI Mismatches: If you encounter errors about missing files (e.g.,../build/Release/pty.node), the module was likely compiled for the wrong architecture or Node ABI [5][8]. Always verify that your rebuild step targets the correct Electron version [6]. - System Requirements: Building native modules requires a C++ compiler and python (typically required by node-gyp) installed on your system [6][2]. If you see errors related to missing build tools (e.g., distutils), ensure your development environment is configured for native compilation [1]. - Hoisted Dependencies: If you are using npm workspaces, ensure that @electron/rebuild can locate node-pty, especially if it is hoisted to the root of your repository [7]. You may need to specify the correct path to the module [7][9].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs/windows-wsl-setup.md around Node setup =="
if [ -f docs/windows-wsl-setup.md ]; then
  awk '{printf "%5d\t%s\n", NR, $0}' docs/windows-wsl-setup.md | sed -n '1,80p'
fi

echo
echo "== README.md around Node/rebuild text =="
if [ -f README.md ]; then
  awk '{printf "%5d\t%s\n", NR, $0}' README.md | sed -n '1,80p'
fi

echo
echo "== package files and dependency clues =="
fd '(^|/)(package.json|package-lock.json|pnpm-lock.yaml|yarn.lock)$' -t f -x sh -c 'echo "--- $1"; awk "{printf \"%5d\\t%s\\n\", NR, \$0}" "$1" | rg -n "engines|node|better-sqlite3|node-pty|electron|rebuild|`@electron/rebuild`" -C 3 || true' sh {}

echo
echo "== repository Node/native module mentions =="
rg -n "Node\.js [0-9]+|Node [0-9]+|node [0-9]+|20 or 22|not 24|24|22|20|better-sqlite3|node-pty|Node\.js LTS|EOL|Maintenance" -S .

echo
echo "== Node release metadata from public endpoint, deterministic parse =="
python3 - <<'PY'
import json, urllib.request
for version in ["22", "24"]:
    url = f"https://nodejs.org/download/release/index.json"
    try:
        data = json.load(urllib.request.urlopen(url, timeout=20))
    except Exception as e:
        print(f"NODE_INDEX_ERROR-{version}: {type(e).__name__}: {e}")
        continue
    versions = [r["version"] for r in data if r["version"].startswith("v" + version + ".")]
    print(f"NODE_VERSION_INDEX-{version}: count={len(versions)} latest={max(versions) if versions else None}")
    major_releases = {v["version"]: {"status": v["status"], "lts": v["lts"], "modules": v.get("modules")} for v in data if v["version"].startswith("v" + version + ".")}
    print("NODE_MAJOR_STATS-", version, {k: major_releases[k] for k in [k for k in major_releases if k.startswith(("v20.", "v22.", "v24."))]})
PY

Repository: guan4tou2/REDLOG

Length of output: 50373


Align the Node.js support policy across both setup documents.

Docs currently direct users from EOL Node.js 20 toward Node 20–22 and exclude Node 24+, while Node 24 is LTS and better-sqlite3 12.1.0+ supports Node 24. Update both files to state what versions Electron's native rebuild is tested for and remove the blanket “no prebuild” claim for 24+ unless the repo toolchain requires source rebuilds.

  • docs/windows-wsl-setup.md#L13-L17
  • README.md#L40-L43
📍 Affects 2 files
  • docs/windows-wsl-setup.md#L13-L17 (this comment)
  • README.md#L40-L43
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/windows-wsl-setup.md` around lines 13 - 17, Align the Node.js
requirements in docs/windows-wsl-setup.md (lines 13-17) and README.md (lines
40-43) with the versions tested by Electron’s native rebuild toolchain. Update
both documents consistently, include Node 24 only if supported by the repository
tooling, and remove the blanket “no prebuilt better-sqlite3 binary” claim unless
source rebuilds are actually required.

Source: MCP tools


```powershell
# Build tools (then tick "Desktop development with C++" in the installer)
winget install Microsoft.VisualStudio.2022.BuildTools

# Node 22 LTS
winget install --id OpenJS.NodeJS.22 -e
```

> If you already have Node 24, remove it first (or use a version manager) so
> `node -v` reports 20.x or 22.x.

### PowerShell notes

- Windows PowerShell 5.1 does **not** support `&&`. Either install PowerShell 7
(`winget install Microsoft.PowerShell`, run as `pwsh`) or chain with
`cmd1; if ($?) { cmd2 }`.
- If `npm` in PowerShell reports *"running scripts is disabled"*, allow user
scripts once: `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`.

---

## 2. Build & run

```powershell
npm install
npm run rebuild # compile better-sqlite3 for Electron's ABI
npm run dev # launch the app
npm run build # production compile (no installer)
```

---

## 3. Packaging (Windows installer)

Installers are produced by [electron-builder](https://www.electron.build)
(config: `electron-builder.yml`; the win target builds an NSIS installer and a
portable exe). Native deps are rebuilt for the target Electron automatically
during packaging. Releases are also produced by the GitHub Actions workflow
(`.github/workflows/release.yml`) on tags.

```powershell
npm run build # compile main/preload/renderer
npx electron-builder --win # NSIS + portable -> dist\
```

**First-run note:** electron-builder downloads `winCodeSign`, which contains
macOS symlinks. Extracting symlinks on Windows needs a privilege standard users
lack. With electron-builder 26 this generally works unattended; if you hit
*"Cannot create symbolic link"*, either enable Windows **Developer Mode**
(Settings → For developers) or run the packaging command once from an elevated
terminal to populate the cache — subsequent builds work without elevation.

Output (installer + `win-unpacked/`) lands in `dist/`.

---

## 4. WSL integration

Pentest tooling often runs in WSL. To let a WSL shell log into RedLog running on
Windows, two things must line up.

### 4.1 Token/port location

RedLog writes `api-token` and `api-port` to the **Windows** user profile
(`%USERPROFILE%\.redlog\`), not WSL's Linux `$HOME`. The hook scripts resolve
this automatically via `%USERPROFILE%` + `wslpath` — note the profile folder
name can differ from `%USERNAME%`, so `%USERPROFILE%` is the reliable anchor.

### 4.2 Networking — mirrored mode is required

The API binds `127.0.0.1` on Windows. Under the default WSL2 **NAT** networking,
WSL's `127.0.0.1` is a separate loopback and **cannot reach it**. Enable
**mirrored networking** so localhost is shared:

1. Create `%USERPROFILE%\.wslconfig`:
```ini
[wsl2]
networkingMode=mirrored
```
2. `wsl --shutdown` (from Windows PowerShell), then reopen WSL.
3. Verify: `wslinfo --networking-mode` → `mirrored`.

To revert, remove that line and `wsl --shutdown` again.

### 4.3 Logging from WSL

Two helper scripts live in `hooks/`:

```bash
# Diagnose the WSL -> RedLog link (env, token path, reachability, round-trip)
bash /mnt/c/Users/<you>/Desktop/REDLOG/hooks/wsl-redlog-test.sh

# Send an event from any script/hook (fire-and-forget; no-ops if unreachable)
hooks/redlog-send.sh "nmap -sV $TARGET" command_start
nmap -sV "$TARGET"
hooks/redlog-send.sh "nmap -sV $TARGET" command_end "{\"exit_code\":$?}"
Comment on lines +111 to +114

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the hook path independent of the working directory.

The example says this can be called from any script/hook, but hooks/redlog-send.sh is relative and only works when the current directory is the repository root. Use a configured absolute repository path, as the preceding diagnostic example does.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/windows-wsl-setup.md` around lines 109 - 112, Update the redlog-send.sh
invocations in the event example to use the configured absolute repository path
established by the preceding diagnostic example, rather than the relative
hooks/redlog-send.sh path. Preserve the existing arguments and fire-and-forget
workflow.

```

`redlog-send.sh` resolves the token path (native or WSL), probes a reachable host
(shared loopback under mirrored networking; the WSL2 gateway otherwise), caches
it, and silently no-ops when RedLog is not running or reachable.

---

## 5. Operational privacy & isolating private activity

RedLog is a passive recorder for an engagement. An operator also does **private**
things on the same machine (personal browsing, personal shells, credentials).
The goal: keep private activity out of the tamper-evident engagement DB.

### 5.1 Instrument only the engagement workspace (primary control)

Isolation is most reliable at the **source** — control *where* producers run,
not just what the UI shows.

- **Dedicated engagement shell/distro.** Source the shell hook (or call
`redlog-send.sh`) **only** in the shell, WSL distro, VM, or OS user you use for
the engagement. Commands you run in your personal shell are never hooked.
- **Hooks fail safe.** Every hook no-ops when RedLog isn't running or the API
isn't reachable, so activity outside an active engagement session isn't logged.
- **Screenshots are deliberate.** Captures are manual / API-triggered, not a
passive desktop grabber — you choose when a screenshot (which may include
private windows) is taken.

A clean pattern on Windows: do all engagement work inside a dedicated **WSL
distro** with the hook sourced in that distro's `~/.bashrc`, and keep personal
work on the Windows host (unhooked).

### 5.2 Pausing — understand the current limitation

The status-bar recording toggle sets a paused flag. **Today this only hides
events from the live timeline — it does not stop database writes.** Any producer
that POSTs to `/api/events` while "paused" is still persisted. Treat the toggle
as *hide*, not *stop*.

For genuine isolation right now, **stop the producer** (unsource/disable the
hook, or close the engagement workspace) rather than relying on pause.

> Planned hardening: gate persistence on the paused flag so the toggle truly
> stops capture, while still recording a pause/resume boundary marker for audit
> integrity; plus per-producer enable/disable in project config. Until then, use
> workspace isolation (§5.1) as the real control.

### 5.3 Per-engagement isolation (built in)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the hyphenated form built-in.

-### 5.3 Per-engagement isolation (built in)
+### 5.3 Per-engagement isolation (built-in)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### 5.3 Per-engagement isolation (built in)
### 5.3 Per-engagement isolation (built-in)
🧰 Tools
🪛 LanguageTool

[grammar] ~160-~160: Use a hyphen to join words.
Context: ... ### 5.3 Per-engagement isolation (built in) Each project is a separate director...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/windows-wsl-setup.md` at line 160, Update the “Per-engagement isolation”
heading to use the hyphenated form “built-in.”

Source: Linters/SAST tools


Each project is a separate directory and SQLite DB
(`~/.redlog/projects/<id>/`), so engagements never cross-contaminate. Close /
switch the project when you stop working an engagement.

### 5.4 Scope

Configure `scope.targets` / `excludeTargets` so out-of-scope hosts are flagged.
Combined with workspace isolation, this keeps the record focused on the
engagement.
102 changes: 102 additions & 0 deletions hooks/redlog-send.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env bash
# RedLog event sender — WSL-aware
# --------------------------------
# Send a single event to the RedLog timeline from any script or hook. Works on
# native Linux/macOS and inside WSL (reaching the RedLog API on the Windows
# host). Fire-and-forget: it silently no-ops (exit 0) if RedLog isn't running
# or isn't reachable, so it never breaks the calling script.
#
# Usage:
# redlog-send.sh "<command>" # subtype defaults to command_start
# redlog-send.sh "<command>" command_end # different subtype
# redlog-send.sh "<command>" command_end '{"exit_code":0}' # extra JSON merged into data{}
# AGENT_TYPE=agent redlog-send.sh "<command>" # override agent_type (default: shell)
#
# Example — wrap a tool in a script hook:
# redlog-send.sh "nmap -sV $TARGET" command_start
# nmap -sV "$TARGET"
# redlog-send.sh "nmap -sV $TARGET" command_end "{\"exit_code\":$?}"

set -uo pipefail

CMD="${1:-}"
SUBTYPE="${2:-command_start}"
EXTRA="${3:-}"
AGENT_TYPE="${AGENT_TYPE:-shell}"
[[ -n "${CMD}" ]] || exit 0

# --- locate api-port / api-token -------------------------------------------
# Native: $HOME/.redlog. WSL: the Windows app writes to the Windows user
# profile, so resolve %USERPROFILE% via wslpath.
_dir=""
if [[ -f "${HOME}/.redlog/api-port" ]]; then
_dir="${HOME}/.redlog"
elif grep -qi microsoft /proc/version 2>/dev/null && command -v wslpath >/dev/null 2>&1; then
_wp="$(cmd.exe /c 'echo %USERPROFILE%' 2>/dev/null | tr -d '\r')"
_wp="$(wslpath "${_wp}" 2>/dev/null || true)"
[[ -n "${_wp}" && -f "${_wp}/.redlog/api-port" ]] && _dir="${_wp}/.redlog"
fi
[[ -n "${_dir}" ]] || exit 0

PORT="$(tr -d '\r\n' < "${_dir}/api-port" 2>/dev/null)"
TOKEN="$(tr -d '\r\n' < "${_dir}/api-token" 2>/dev/null)"
[[ -n "${PORT}" && -n "${TOKEN}" ]] || exit 0

# --- resolve a reachable host (cached to avoid re-probing every call) -------
# Under WSL2 mirrored networking / WSL1, 127.0.0.1 is shared. Under NAT, try
# the default gateway (Windows host) — reachable only if the API isn't bound to
# loopback-only; otherwise this sender simply no-ops.
_cache="${TMPDIR:-/tmp}/.redlog-host-${PORT}"
HOST=""
if [[ -f "${_cache}" ]]; then
HOST="$(cat "${_cache}" 2>/dev/null)"
curl -sf --connect-timeout 1 --max-time 2 "http://${HOST}:${PORT}/api/health" >/dev/null 2>&1 || HOST=""
Comment on lines +49 to +53

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Store and validate the cached host securely.

Line 49 uses a predictable shared /tmp cache. Another local user can pre-seed it with an attacker host; after its fake health response, lines 97-99 send the RedLog bearer token there. Use a per-user 0700 cache directory and only accept 127.0.0.1 or the currently resolved gateway as cached values.

Proposed direction
- _cache="${TMPDIR:-/tmp}/.redlog-host-${PORT}"
+ _cache_dir="${XDG_CACHE_HOME:-${HOME}/.cache}/redlog"
+ (umask 077 && mkdir -p "${_cache_dir}") || exit 0
+ _cache="${_cache_dir}/host-${PORT}"

Also resolve _gw before reading the cache and discard cached values other than 127.0.0.1 or "${_gw}".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hooks/redlog-send.sh` around lines 49 - 53, Update the cache setup in
redlog-send.sh to use a per-user cache directory with 0700 permissions instead
of the predictable shared /tmp path, and resolve _gw before reading the cache.
In the cached-host validation near _cache and HOST, accept only 127.0.0.1 or the
resolved _gw value before performing the health check; discard all other cached
hosts.

fi
if [[ -z "${HOST}" ]]; then
_gw="$(ip route show default 2>/dev/null | awk '{print $3; exit}')"
for _c in 127.0.0.1 "${_gw}"; do
[[ -z "${_c}" ]] && continue
if curl -sf --connect-timeout 1 --max-time 2 "http://${_c}:${PORT}/api/health" >/dev/null 2>&1; then
HOST="${_c}"
echo "${_c}" > "${_cache}" 2>/dev/null || true
break
fi
done
fi
[[ -n "${HOST}" ]] || exit 0

# --- build payload ----------------------------------------------------------
if command -v python3 >/dev/null 2>&1; then
PAYLOAD="$(CMD="${CMD}" SUBTYPE="${SUBTYPE}" EXTRA="${EXTRA}" AGENT_TYPE="${AGENT_TYPE}" python3 -c '
import json, os
src = "native"
try:
if "microsoft" in open("/proc/version").read().lower():
src = "wsl"
except Exception:
pass
d = {"agent_type": os.environ["AGENT_TYPE"],
"data": {"subtype": os.environ["SUBTYPE"],
"command": os.environ["CMD"],
"shell": os.path.basename(os.environ.get("SHELL", "")),
"source": src}}
ex = os.environ.get("EXTRA", "")
if ex:
try:
d["data"].update(json.loads(ex))
except Exception:
pass
print(json.dumps(d))')" || exit 0
else
# Minimal fallback without python3 (no EXTRA merge; basic escaping).
_esc() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'; }
PAYLOAD="{\"agent_type\":\"${AGENT_TYPE}\",\"data\":{\"subtype\":\"${SUBTYPE}\",\"command\":\"$(_esc "${CMD}")\",\"source\":\"native\"}}"
fi
Comment on lines +90 to +94

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not hand-roll JSON escaping in the fallback.

The fallback only escapes \ and ". A multiline command, tab, or other control character produces invalid JSON, so the hook silently drops the event. Use a real JSON encoder (for example jq -n) when Python is unavailable, or explicitly require Python.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hooks/redlog-send.sh` around lines 90 - 94, Replace the hand-rolled _esc JSON
construction in the Python-unavailable fallback of hooks/redlog-send.sh with a
real JSON encoder such as jq -n, passing AGENT_TYPE, SUBTYPE, and CMD as encoded
values so multiline text, tabs, quotes, backslashes, and other control
characters remain valid JSON; alternatively, require Python instead of emitting
an unsafe payload.


# --- fire-and-forget send ---------------------------------------------------
curl -sf -X POST "http://${HOST}:${PORT}/api/events" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "${PAYLOAD}" \
--connect-timeout 1 --max-time 2 >/dev/null 2>&1 || true
exit 0
Loading