Skip to content

Local Deep Research

v1.6.11 Security

This release includes 3 security fixes for security teams reviewing exposed deployments.

Published 17d LLM Frameworks
✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →
This release patches 3 known CVEs

Topics

academia anthropic arxiv brave deep-research encryption
+14 more
home-automation homeserver local local-deep-research local-llm mistral ollama openai pubmed research research-tool retrieval-augmented-generation searxng self-hosted

Affected surfaces

auth rce_ssrf

ReleasePort's take

Moderate signal
editorial:auto 9d

Release v1.6.11 blocks cloud‑metadata IP in Apprise plugin schemes and redacts Google Gemini API keys from error logs.

Why it matters: Security hardening now prevents unauthorized metadata access via Apprise and eliminates exposed Gemini credentials; no measurable adoption metric provided.

Summary

AI summary

Extended cloud‑metadata IP block to Apprise plugin schemes and redacted Google Gemini API key from logs.

Changes in this release

Security Medium

Suppressed CVE-2026-8328 (ftplib.ftpcp SSRF) until Python 3.14.6.

Suppressed CVE-2026-8328 (ftplib.ftpcp SSRF) until Python 3.14.6.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: high

Security Medium

Extended cloud-metadata IP absolute-block to Apprise plugin schemes in NotificationURLValidator.

Extended cloud-metadata IP absolute-block to Apprise plugin schemes in NotificationURLValidator.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Security Medium

Redacted Google Gemini API key from list_models_for_api error log.

Redacted Google Gemini API key from list_models_for_api error log.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Breaking Medium

JavaScript rendering disabled by default in production Docker image; new web.enable_javascript_rendering setting (default false).

JavaScript rendering disabled by default in production Docker image; new web.enable_javascript_rendering setting (default false).

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: high

Feature Medium

Embeddings now work against OpenAI-compatible local servers (LM Studio, vLLM, llama.cpp).

Embeddings now work against OpenAI-compatible local servers (LM Studio, vLLM, llama.cpp).

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: high

Feature Medium

Enriched AI release notes and rendered changelog in release flow.

Enriched AI release notes and rendered changelog in release flow.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Feature Medium

Added statistical functions module for benchmarks.

Added statistical functions module for benchmarks.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Feature Medium

Surface WAL-dispose failures and documented LDR_APP_DEBUG sensitivity.

Surface WAL-dispose failures and documented LDR_APP_DEBUG sensitivity.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Dependency Medium

Pinned towncrier to exact version and bumped Python to 3.14.5.

Pinned towncrier to exact version and bumped Python to 3.14.5.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Performance Medium

Added timeout=8 to Docker HEALTHCHECK urllib.request.urlopen call.

Added timeout=8 to Docker HEALTHCHECK urllib.request.urlopen call.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: high

Bugfix Medium

"Start Research" button no longer silently does nothing for non-multiple-of-512 context window values.

"Start Research" button no longer silently does nothing for non-multiple-of-512 context window values.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: high

Bugfix Medium

Chinese/Japanese/Korean text now renders in exported PDFs.

Chinese/Japanese/Korean text now renders in exported PDFs.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: high

Bugfix Medium

Cross-engine filtering now keeps LLM ranking fallbacks within the subset of results the model actually evaluated.

Cross-engine filtering now keeps LLM ranking fallbacks within the subset of results the model actually evaluated.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: high

Bugfix Medium

Fixed file-descriptor leak (`anon_inode:[eventpoll]`) in `_close_base_llm` when invoked while an asyncio loop is running.

Fixed file-descriptor leak (`anon_inode:[eventpoll]`) in `_close_base_llm` when invoked while an asyncio loop is running.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Refactor Medium

Refactored environment settings to use specialized exception classes.

Refactored environment settings to use specialized exception classes.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Other Medium

Added a "Testing a Release Candidate (Prerelease Image)" subsection to the developer guide.

Added a "Testing a Release Candidate (Prerelease Image)" subsection to the developer guide.

Source: granite4.1:8b-q6_K@2026-05-21

Confidence: low

Full changelog

🔒 Security

  • Extended the cloud-metadata IP absolute-block in NotificationURLValidator to Apprise plugin schemes (signal, gotify, ntfy/ntfys, mattermost, rocketchat, matrix, json, xml, form, mailto). Previously only the http/https branch ran the IMDS guard, so a notification URL like signal://169.254.169.254/... would round-trip to Apprise even though Apprise translates it into a plain HTTP request against that host. LAN/loopback reach for self-hosted plugin endpoints (the #4006 use case) is unchanged.
  • Fix Google Gemini API key leak in list_models_for_api error log. The
    URL constructed at request time embeds the key as a ?key=... query
    parameter (per Google's documented API), so when the underlying request
    failed, the requests exception message — and therefore the
    logger.exception(...) traceback — contained the full URL with the key.
    The except handler now redacts the key from the message and uses
    logger.warning (no exception chain).

🐛 Bug Fixes

  • JavaScript rendering disabled by default in the production Docker image (#3826). The headless-browser fallback in the content fetcher (Crawl4AI/Playwright) was previously attempted on every fetch even though the default Docker production image ships without a Chromium binary - each attempt failed loudly and contributed to the memory growth reported in issue #3826. A new web.enable_javascript_rendering setting (default false) gates the fallback. In limited (mostly accidental) internal benchmark comparisons - between dev instances that happened to have Chromium installed and routine Docker runs that did not - JS rendering did not measurably improve research quality, and most regular benchmark runs are on Docker without Chromium anyway, so the change does not regress observed quality. To enable: install Chromium in the runtime environment (playwright install --with-deps chromium) and toggle the setting in the UI. (#3826)

  • Embeddings now work against OpenAI-compatible local servers (LM Studio, vLLM, llama.cpp). Previously the OpenAI embeddings provider only listed itself as available when an API key was configured, and the relevant embeddings.openai.* settings were not registered for the UI to surface — so users who ran an OpenAI-compatible local server had no way to point the embeddings tab at it. The provider now also reports available when only embeddings.openai.base_url is set, substitutes a placeholder API key for keyless endpoints (matching the LM Studio LLM provider's behavior), and ships a new settings_openai_embeddings.json defaults file that registers embeddings.openai.api_key, embeddings.openai.base_url, embeddings.openai.model, and embeddings.openai.dimensions. The model-list lookup now routes through the configured base_url so model discovery also targets the local server. (#3883)

  • "Start Research" button no longer silently does nothing for users with a non-multiple-of-512 context window value. The hidden context_window input on the research form had a step="512" constraint, so any stored value not aligned to that grid (e.g. 25000) failed HTML5 form validation. Because the input lives inside a display:none container that is only shown for local providers, the browser could not focus the invalid field to report the error, so submission was aborted with no visible message and no log line — the click appeared to do nothing. Relaxed the step to 1 so any in-range integer is accepted; the value is still bounded by min/max and persisted unchanged. (#3909)

  • Chinese/Japanese/Korean text now renders in exported PDFs. The default PDF stylesheet hard-coded a Latin-only font stack, so any CJK characters in the research result were dropped silently from the download even though they displayed correctly in the browser. The minimal CSS now includes a broad CJK fallback chain (Noto Sans CJK, PingFang, Hiragino, Apple SD Gothic Neo, Microsoft YaHei/JhengHei, Yu Gothic, Malgun Gothic, SimSun) covering Windows, macOS, and Linux desktops out of the box, and the Docker image now installs fonts-noto-cjk so the slim base image has glyph coverage. Linux pip/server installs still need a CJK font package on the host — see install-pip.md and the FAQ for the per-distro commands. (#4055)

  • Cross-engine filtering now keeps LLM ranking fallbacks within the subset of results the model actually evaluated, preventing unevaluated search results from leaking into downstream synthesis.

  • Fix file-descriptor leak (anon_inode:[eventpoll]) in _close_base_llm when invoked while an asyncio loop is running. The previous code path skipped the async-client close in that case and documented that the "loop owner" would close instead — but no loop-owner cleanup actually exists in the project, so the inner httpx.AsyncClient (and its epoll_create FD) was silently abandoned. The same skip-path fires under the default langgraph-agent strategy because LangGraph dispatches some tool steps via asyncio internally, so close calls reached from a sync finally can still land inside a live loop. Cleanup now runs in a brief daemon thread that owns its own loop, so asyncio.run(aclose()) works regardless of the caller's loop state; a bounded 5-second join keeps it from holding up shutdown if the Ollama server is unresponsive. This is the gap left by #3855's coverage of #3816 — same leak class, different code path.

    Also fixes a smaller secondary leak in the Docker HEALTHCHECK: urllib.request.urlopen('http://localhost:5000/api/v1/health') had no timeout= argument, so when the app slowed down (FD exhaustion or otherwise) Docker's 10s healthcheck timeout SIGKILL'd the sh -c wrapper but the python child got reparented to PID 1 and hung forever, each contributing a pidfd and a TCP socket against the app. Adding timeout=8 lets the child return/raise before Docker's wall so it exits cleanly and gets reaped.

📝 Other Changes

  • Added a "Testing a Release Candidate (Prerelease Image)" subsection to the developer guide (docs/developing.md) covering both the floating :prerelease and immutable prerelease-vX.Y.Z-<sha> Docker Hub tags, with a side-by-side compose snippet that runs the RC on port 5001 with isolated volumes so a broken migration cannot affect a production instance.
  • Pool-dispose failures in the periodic WAL/SHM handle-release workaround (ADR-0004) now log at WARNING instead of DEBUG so silent drift becomes visible. Documented in docs/CONFIGURATION.md that LDR_APP_DEBUG=true also enables Loguru diagnose=True, which can materialise sensitive locals into exception traces — do not enable in production.
  • Refactored environment settings to use specialized exception classes, improving error observability and alignment with TRY003 standards.

What's Changed

🔒 Security Updates

  • fix(security): pin towncrier to exact version and bump Python to 3.14.5 by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4046
  • ci(security): close zizmor artipacked finding + dismiss platform-conditional CVE by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4050
  • test(security): SSRF edge-case coverage and weak-test cleanup by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4062
  • fix(security): extend IMDS absolute-block to Apprise plugin schemes by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4063
  • chore(security): suppress CVE-2026-8328 (ftplib.ftpcp SSRF) until 3.14.6 by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4072
  • fix(security): redact Google API key from list_models error log by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4070

✨ New Features

  • fix(release): enrich AI release notes + render changelog in release flow by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4035
  • feat(benchmarks): add statistical functions module by @ishitta-iyer in https://github.com/LearningCircuit/local-deep-research/pull/4029
  • chore(observability): surface WAL-dispose failures + document LDR_APP_DEBUG sensitivity by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4042
  • Improve UI tests + CI: artifact uploads, WebKit skip narrowing, settle-wait migrations by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4061
  • test(ui): chunk mobile-nav overlap DOM walk; drop WebKit skip (#4060) by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4076

🐛 Bug Fixes

  • fix(embeddings): correct OpenAIEmbeddingsProvider.requires_api_key to… by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4036
  • fix(llm,docker): close ChatOllama async httpx client when called from a running loop + healthcheck timeout by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4047
  • fix(ci): drop environment: ci from reusable workflow by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4049
  • fix(research-form): relax context_window step so Start Research submits (#3909) by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4051
  • fix(pdf): render CJK characters in exported PDFs (#4055) by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4058
  • fix(content-fetcher): disable JS rendering by default (#3826) by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/3971
  • fix(release): set towncrier name to skip package import by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4071
  • fix(ui-tests): match create/new/add buttons with word boundaries by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4069

🗄️ Database Changes

  • test(migrations): pin invariants from PR #4000 multi-round review by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4033
  • chore(alembic-runner): drop stale isolation_level="IMMEDIATE" references by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4039

📚 Documentation

  • docs(developing): add prerelease Docker image testing section by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4034
  • follow-up to #4047: hoist asyncio+threading imports + document Wave 7 in resource-cleanup.md by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4048
  • docs(resource-cleanup): expand Wave 7 with full audit ledger by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4054

🔧 CI/CD & Maintenance

  • 🤖 Update dependencies by @github-actions[bot] in https://github.com/LearningCircuit/local-deep-research/pull/4031
  • 🤖 Update dependencies by @github-actions[bot] in https://github.com/LearningCircuit/local-deep-research/pull/4043
  • chore: bump patch version to 1.6.11 by @github-actions[bot] in https://github.com/LearningCircuit/local-deep-research/pull/3961

🧹 Code Quality & Refactoring

  • test(e2e): tolerate brief LLM output in research export test by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4053
  • chore(labels): add 'code-ready' as a human-only signal label by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4068

🧪 Tests

  • test(e2e): regression test for #3909 hidden context_window blocking submit by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4059
  • test(scheduler): credential lifecycle coverage and weak-test cleanup by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4065
  • test(error_handling): pin load-bearing branches in openai_compat_errors by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4074
  • test(llm_utils): pin daemon-thread contract for in-loop async close by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4078

Other Changes

  • feat(errors): friendly runtime messages for OpenAI-compatible endpoints by @voidborne-d in https://github.com/LearningCircuit/local-deep-research/pull/4027
  • fix(embeddings): allow OpenAI-compatible local endpoints (#3883) by @SuperMarioYL in https://github.com/LearningCircuit/local-deep-research/pull/4026
  • fix(search): keep cross-engine filter fallback within evaluated context by @qWaitCrypto in https://github.com/LearningCircuit/local-deep-research/pull/3866
  • Feat/deepseek provider by @kwhyte7 in https://github.com/LearningCircuit/local-deep-research/pull/3432
  • fix(notifications): allow signal:// Apprise scheme (#4006) by @therahul-yo in https://github.com/LearningCircuit/local-deep-research/pull/4056
  • refactor: use specialized exception classes in settings module by @RinZ27 in https://github.com/LearningCircuit/local-deep-research/pull/3838
  • chore(ci): cut workflow-status.md regen diff noise by @LearningCircuit in https://github.com/LearningCircuit/local-deep-research/pull/4066

New Contributors

  • @voidborne-d made their first contribution in https://github.com/LearningCircuit/local-deep-research/pull/4027
  • @qWaitCrypto made their first contribution in https://github.com/LearningCircuit/local-deep-research/pull/3866
  • @therahul-yo made their first contribution in https://github.com/LearningCircuit/local-deep-research/pull/4056
  • @RinZ27 made their first contribution in https://github.com/LearningCircuit/local-deep-research/pull/3838

Full Changelog: https://github.com/LearningCircuit/local-deep-research/compare/v1.6.10...v1.6.11

Security Fixes

  • Extended cloud-metadata IP absolute‑block in `NotificationURLValidator` to cover Apprise plugin schemes (signal, gotify, ntfy/ntfys, mattermost, rocketchat, matrix, json, xml, form, mailto).
  • Redacted Google Gemini API key from error log messages and changed logging level to warning.
  • CVE-2026-8328

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track Local Deep Research

Get notified when new releases ship.

Sign up free

About Local Deep Research

AI-powered deep research tool with multi-source search (arXiv, PubMed, web)

All releases →

Related context

Related CVEs

Beta — feedback welcome: [email protected]