loki-mode 7.78.0 → 7.80.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/checkpoint_sync.py +189 -0
- package/autonomy/lib/config-map.sh +955 -0
- package/autonomy/loki +273 -8
- package/autonomy/run.sh +230 -195
- package/autonomy/sandbox.sh +98 -0
- package/autonomy/trigger-server.py +419 -43
- package/dashboard/__init__.py +1 -1
- package/dashboard/registry.py +212 -0
- package/dashboard/server.py +299 -7
- package/dashboard/static/index.html +383 -150
- package/docs/CONFIG-FILE-PLAN.md +459 -0
- package/docs/ENTERPRISE-IDENTITY-ROADMAP.md +206 -0
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/loki.js +2 -2
- package/lokistore/__init__.py +65 -0
- package/lokistore/base.py +172 -0
- package/lokistore/cloud.py +305 -0
- package/lokistore/factory.py +187 -0
- package/lokistore/local.py +219 -0
- package/mcp/__init__.py +1 -1
- package/memory/retrieval.py +147 -0
- package/memory/tree_index.py +499 -0
- package/memory/tree_search.py +305 -0
- package/package.json +2 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
package/autonomy/sandbox.sh
CHANGED
|
@@ -115,6 +115,88 @@ log_error() {
|
|
|
115
115
|
echo -e "${RED}[SANDBOX]${NC} $1" >&2
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
#===============================================================================
|
|
119
|
+
# A5: ALLOWED_PATHS + BLOCKED_COMMANDS enforcement (sandbox-local)
|
|
120
|
+
#
|
|
121
|
+
# sandbox.sh runs as its own process (set -euo pipefail) and does NOT source
|
|
122
|
+
# run.sh, so the run.sh helpers are out of scope. These are self-contained
|
|
123
|
+
# mirrors that read the same env vars (LOKI_ALLOWED_PATHS / LOKI_BLOCKED_COMMANDS)
|
|
124
|
+
# so the sandbox -- the real security boundary -- enforces them at the point
|
|
125
|
+
# where run.sh/sandbox.sh actually controls the surface: which host paths get
|
|
126
|
+
# bind-mounted, and the operator-supplied `loki sandbox run <cmd>` argv.
|
|
127
|
+
#
|
|
128
|
+
# HONEST SCOPE: this does NOT restrict provider-driven agent writes inside the
|
|
129
|
+
# container (those are contained by cap-drop/seccomp/read-only mounts, not a
|
|
130
|
+
# path allowlist). It is defense-in-depth on the surfaces run.sh owns.
|
|
131
|
+
#===============================================================================
|
|
132
|
+
|
|
133
|
+
# Return 0 if target is within LOKI_ALLOWED_PATHS (or the allowlist is unset/
|
|
134
|
+
# empty -> no restriction). realpath -m collapses ../ traversal before the
|
|
135
|
+
# prefix check, so allowed/../../etc cannot slip past.
|
|
136
|
+
_sandbox_path_within_allowed() {
|
|
137
|
+
local target="$1"
|
|
138
|
+
local allow="${LOKI_ALLOWED_PATHS:-}"
|
|
139
|
+
[ -z "$allow" ] && return 0
|
|
140
|
+
[ -z "$target" ] && return 1
|
|
141
|
+
|
|
142
|
+
local norm_target
|
|
143
|
+
norm_target="$(realpath -m -- "$target" 2>/dev/null || true)"
|
|
144
|
+
if [ -z "$norm_target" ]; then
|
|
145
|
+
norm_target="$(LOKI_AP_T="$target" python3 -c 'import os; print(os.path.realpath(os.environ["LOKI_AP_T"]))' 2>/dev/null || true)"
|
|
146
|
+
fi
|
|
147
|
+
[ -z "$norm_target" ] && return 1
|
|
148
|
+
|
|
149
|
+
local entry norm_entry
|
|
150
|
+
IFS=',' read -ra _AP_ARRAY <<< "$allow"
|
|
151
|
+
for entry in "${_AP_ARRAY[@]}"; do
|
|
152
|
+
entry="${entry#"${entry%%[![:space:]]*}"}"
|
|
153
|
+
entry="${entry%"${entry##*[![:space:]]}"}"
|
|
154
|
+
[ -z "$entry" ] && continue
|
|
155
|
+
# Tilde-expand allowlist entries so "~/proj" matches a resolved $HOME path.
|
|
156
|
+
# SC2088 disabled intentionally: we are pattern-MATCHING a literal "~/" at
|
|
157
|
+
# the start of a config-supplied string (no expansion wanted here), then
|
|
158
|
+
# expanding it ourselves via $HOME. The directive sits in front of the whole
|
|
159
|
+
# if-block (it cannot precede an elif branch -- SC1123).
|
|
160
|
+
# shellcheck disable=SC2088
|
|
161
|
+
if [[ "$entry" == "~/"* ]]; then
|
|
162
|
+
entry="$HOME/${entry#\~/}"
|
|
163
|
+
elif [[ "$entry" == "~" ]]; then
|
|
164
|
+
entry="$HOME"
|
|
165
|
+
fi
|
|
166
|
+
norm_entry="$(realpath -m -- "$entry" 2>/dev/null || true)"
|
|
167
|
+
if [ -z "$norm_entry" ]; then
|
|
168
|
+
norm_entry="$(LOKI_AP_E="$entry" python3 -c 'import os; print(os.path.realpath(os.environ["LOKI_AP_E"]))' 2>/dev/null || true)"
|
|
169
|
+
fi
|
|
170
|
+
[ -z "$norm_entry" ] && continue
|
|
171
|
+
if [ "$norm_target" = "$norm_entry" ] || [ "${norm_target#"${norm_entry}/"}" != "$norm_target" ]; then
|
|
172
|
+
return 0
|
|
173
|
+
fi
|
|
174
|
+
done
|
|
175
|
+
return 1
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Return 1 (block) if the command string contains a blocked pattern. Mirrors
|
|
179
|
+
# run.sh check_command_allowed; default list matches run.sh's BLOCKED_COMMANDS.
|
|
180
|
+
_sandbox_command_allowed() {
|
|
181
|
+
local command="$1"
|
|
182
|
+
# NOTE: the default list is assigned to a separate var first. It cannot be an
|
|
183
|
+
# inline ${VAR:-default} default because the default value contains a literal
|
|
184
|
+
# "}" (the fork-bomb ":(){ :|:& };:"), which would prematurely terminate the
|
|
185
|
+
# ${...} parameter expansion and corrupt the list.
|
|
186
|
+
local _default_blocked='rm -rf /,dd if=,mkfs,:(){ :|:& };:'
|
|
187
|
+
local blocked_list="${LOKI_BLOCKED_COMMANDS:-$_default_blocked}"
|
|
188
|
+
local blocked
|
|
189
|
+
IFS=',' read -ra _BC_ARRAY <<< "$blocked_list"
|
|
190
|
+
for blocked in "${_BC_ARRAY[@]}"; do
|
|
191
|
+
[ -z "$blocked" ] && continue
|
|
192
|
+
if [[ "$command" == *"$blocked"* ]]; then
|
|
193
|
+
log_error "SECURITY: blocked dangerous command (matched '$blocked'): $command"
|
|
194
|
+
return 1
|
|
195
|
+
fi
|
|
196
|
+
done
|
|
197
|
+
return 0
|
|
198
|
+
}
|
|
199
|
+
|
|
118
200
|
# Check if a port is available
|
|
119
201
|
check_port_available() {
|
|
120
202
|
local port="$1"
|
|
@@ -1156,6 +1238,15 @@ start_sandbox() {
|
|
|
1156
1238
|
c_host="$HOME"
|
|
1157
1239
|
fi
|
|
1158
1240
|
if [[ -e "$c_host" ]]; then
|
|
1241
|
+
# A5: when LOKI_ALLOWED_PATHS is set, reject a custom mount
|
|
1242
|
+
# whose host path is outside the allowlist. This is a write
|
|
1243
|
+
# surface run.sh/sandbox.sh genuinely controls (we decide what
|
|
1244
|
+
# to bind into the container), so the allowlist is enforced
|
|
1245
|
+
# here rather than only documented.
|
|
1246
|
+
if ! _sandbox_path_within_allowed "$c_host"; then
|
|
1247
|
+
log_error "Custom mount host path is outside LOKI_ALLOWED_PATHS, refusing to mount: $c_host"
|
|
1248
|
+
return 1
|
|
1249
|
+
fi
|
|
1159
1250
|
docker_args+=("--volume" "${c_host}:${c_container}:${c_mode}")
|
|
1160
1251
|
log_info " Custom mount: $c_host -> $c_container ($c_mode)"
|
|
1161
1252
|
else
|
|
@@ -1351,6 +1442,13 @@ sandbox_run() {
|
|
|
1351
1442
|
return 1
|
|
1352
1443
|
fi
|
|
1353
1444
|
|
|
1445
|
+
# A5: defense-in-depth on the operator-supplied command. The container is the
|
|
1446
|
+
# real boundary, but an obviously-destructive command is blocked here before
|
|
1447
|
+
# it is dispatched into the sandbox (honors LOKI_BLOCKED_COMMANDS).
|
|
1448
|
+
if ! _sandbox_command_allowed "$cmd"; then
|
|
1449
|
+
return 1
|
|
1450
|
+
fi
|
|
1451
|
+
|
|
1354
1452
|
check_docker
|
|
1355
1453
|
|
|
1356
1454
|
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|