v0.2.5

tinyauthapp/tinyauthv0.2.5Jul 6, 2026by het0814

AI Summary

This major security hardening pass fixes path traversal vulnerabilities, CORS credential exposure, and gates destructive UI endpoints to localhost, while collapsing the legacy scope model to a single `agent_id`.

Key Highlights

  • Path traversal fixes for filesystem paths using `agent_id` and `session_id`
  • CORS credential exposure fix to prevent arbitrary origin mirroring
  • Sensitive Web UI endpoints gated to localhost-only
  • Unpredictable session secret generation replacing hardcoded defaults
  • Collapse of legacy scope/namespace model to a single `agent_id`

New Features

  • Collapse of legacy scope model to `agent_id`
  • Manual conflict resolution validation requiring non-empty content
  • CLI honors custom title for short memories
  • Session token lifecycle hardening with strict validation

Full Release Notes

# Release Notes for v0.2.5

This release is a **major security hardening pass** across the API, CLI, and Web UI: path-traversal fixes wherever `agent_id`/`session_id`/dates flow into filesystem paths, a CORS credential-exposure fix, localhost-only gating for destructive UI endpoints, unpredictable session-secret generation, stricter session-token validation, and a large cleanup that collapses the legacy scope/namespace model down to a single `agent_id`.

## Security

- **Path traversal via `agent_id` / `session_id` / dates** (`memanto/app/utils/validation.py`,
  `memanto/app/services/{session_service,memory_export_service,daily_analysis_service}.py`)
  - Unsanitized `agent_id`/`session_id` values were concatenated directly into
    `pathlib.Path` expressions (e.g. `sessions_dir / f"{agent_id}.json"`),
    letting a caller escape the storage directory with input like
    `"../../etc/passwd"`. All filesystem call-sites now run through
    `validate_safe_id()`.
  - Extended the same guard to `memory_export_service`, `daily_analysis_service`
    (including the date parameter used in glob patterns and JSON output paths),
    and the `--output-path` CLI flag (anchored to a base dir via `relative_to()`
    containment checks).

- **CORS reflected-origin credential exposure** (`memanto/app/main.py`)
  - Default `ALLOWED_ORIGINS=["*"]` combined with `allow_credentials=True`
    caused Starlette to mirror any request `Origin` back in
    `Access-Control-Allow-Origin` while also sending
    `Access-Control-Allow-Credentials: true` — letting any website make
    credentialed cross-origin requests to the Memanto API. Fixed to stop
    mirroring arbitrary origins when credentials are allowed.

- **Sensitive Web UI endpoints gated to localhost-only** (`memanto/app/ui/routes/ui_router.py`)
  - `POST /api/ui/shutdown`, `GET /api/ui/browse`, `PATCH /api/ui/config`,
    `PUT /api/ui/api-key`, `POST /api/ui/onprem/restart`, plus the
    connections and migrate endpoints, were reachable from any network host
    with no authentication (remote DoS, arbitrary file listing, config/API-key
    overwrite). Added a `_require_local()` dependency that returns 403 for any
    caller that isn't `127.0.0.1`/`::1` (including IPv4-mapped IPv6 loopback),
    and blocked glob-pattern injection in the browse endpoint.

- **Unpredictable session secret** (`memanto/app/config.py`, `memanto/app/services/session_service.py`)
  - Removed the hardcoded default JWT signing secret
    (`"memanto-default-secret-change-in-production"`). When
    `MEMANTO_SECRET_KEY` isn't set, a per-instance random secret is now
    generated (`secrets.token_hex(32)`) and persisted locally instead of
    falling back to a publicly-known constant.

- **Session token lifecycle hardening** (`memanto/app/services/session_service.py`,
  `memanto/app/routes/memory.py`, `memanto/cli/client/{direct_client,sdk_client}.py`)
  - Deactivated/terminated session tokens are now rejected outright (401)
    instead of continuing to authorize writes.
  - Cross-agent session/agent mismatches now consistently raise
    `AuthorizationError` → HTTP 403 (was a generic 500) across all
    session-scoped endpoints.
  - CLI clients now validate cached sessions before reuse instead of trusting
    a stale cached token.
  - TypeScript SDK resets its local session state after `deleteAgent()`.

## Improvements

- **Legacy scope model collapsed to `agent_id`** (`memanto/app/core.py`,
  `memanto/app/models/__init__.py`, `memanto/app/constants.py`,
  `memanto/app/services/*.py`)
  - Removed the `scope_type`/`scope_id` pair (and the underlying
    `MemoryScope`/namespace-parsing machinery) in favor of a single `agent_id`
    field; namespaces are now built by one free function
    (`agent_namespace(agent_id)`). Dead code from this and prior cleanups moved
    to `memanto/app/legacy/` (excluded from CI lint/type-check).
  - Removed orphaned "trust" fields that were never populated by any live
    write/read path (`superseded_by`, `validation_count`,
    `contradiction_detected`, etc.) and the unused `ValidationPolicy` class.

- **Confidence filtering bug fix** (`memanto/app/services/memory_read_service.py`)
  - Numeric `min_confidence` filtering in memory search previously relied on
    Moorcheh keyword syntax that never matched; it's now applied as a
    post-filter on the numeric `confidence` field, fixing a zero-threshold
    edge case that silently returned nothing.

- **Manual conflict resolution validation** (`memanto/app/models/__init__.py`)
  - `ConflictResolveRequest` now requires non-empty `manual_content` when
    `action == "manual"`, both on the API and in the UI (blank submissions show
    a warning toast and refocus the textarea).

- **Timestamp normalization** (`memanto/app/utils/temporal_helpers.py`)
  - Parsed ISO timestamps are now consistently normalized to UTC, whether the
    input has an explicit offset or is naive.

- **CLI: honor custom title for short memories** (`memanto/cli/commands/memory.py`)
  - `memanto remember` no longer overrides an explicit `--title` with an
    auto-truncated content snippet when the memory content is short.

- **Memory deletion response handling** (`memanto/app/services/memory_write_service.py`)
  - Tightened success/failure detection for memory deletion and update-then-delete
    flows so partial or malformed backend responses aren't reported as success.

- **TypeScript SDK & CI** — updated dependencies, CI workflow permissions,
  regenerated `openapi.json`, and added an npm publishing workflow
  (`.github/workflows/publish.yml`).

## Tests

- New `tests/test_cors_fix.py`, `tests/test_output_path_traversal.py`,
  `tests/test_ui_auth.py`, `tests/test_remaining_ui_auth.py` covering the CORS,
  path-traversal, and localhost-gating fixes.
- New `tests/test_memory_read_confidence.py` and `tests/test_temporal_helpers.py`.
- Expanded `tests/test_api.py`, `tests/test_cli.py`, `tests/test_unit.py` for
  session-secret generation, inactive-token rejection, and deletion handling.

## Full Changelog

Full Changelog: https://github.com/moorcheh-ai/memanto/compare/v0.2.4...v0.2.5