Core services security audit and fix plan¶
Status: Current | Last reviewed: 2026-09-17
This report covers security vulnerabilities, not just open-source licenses, in the local ai-orchestrator, ai-manager, surface-app and async-agent versions. Use it to sequence remediation and distinguish findings from scan coverage gaps. It records the baseline; the broader fixes below have not been applied locally.
Results¶
Each cell is Critical / High / Medium. Low findings are excluded. Dependency counts deduplicate advisory IDs within a service. Source counts are Snyk Code finding identities. The same advisory can affect multiple services; do not interpret the sum as distinct vulnerabilities or proven exploits.
| Local service | Dependency vulnerabilities | Source security findings | Container OS findings |
|---|---|---|---|
| ai-orchestrator | 2 / 25 / 20 | 0 / 0 / 6 | 3 / 10 / 4, base stage only |
| ai-manager | 4 / 34 / 48 | 0 / 25 / 34 | 0 / 0 / 0 |
| surface-app | 6 / 40 / 52 | 0 / 4 / 9 | 0 / 0 / 0 |
| async-agent | 0 / 0 / 0 | 0 / 1 / 0 | 3 / 10 / 4 |
There are 231 dependency advisory instances, 79 source findings and 34 container advisory instances across these service/scan combinations. The full CSV inventory includes every finding ID, severity, affected version, reported fixed versions, dependency paths or source location, and initial review status. The JSON summary preserves the counts.
The scans also produced 24 license-policy notices, listed separately in the CSV: ai-orchestrator has four High and three Medium; ai-manager and surface-app each have seven Medium; async-agent has three Medium. These are excluded from every security count above.
What was scanned¶
- Snyk Open Source: known security advisories in dependency graphs. Node scans include development dependencies; the CSV identifies findings reachable only through development dependencies. For example, surface-app's Critical esbuild, flatted and Vitest findings belong to its development toolchain, while its Next.js and undici findings have runtime dependency paths.
- Snyk Code: static analysis for SQL injection, XSS, SSRF, code injection, unsafe redirects, prototype pollution, regex denial of service, JWT handling and related risks. Tests and public diagnostic assets are included.
- Snyk Container: OS packages in freshly built images. The frontend reports enumerate Alpine packages; zero OS findings does not mean their Next.js/application dependencies are clean. Those appear in the dependency scan.
Scans used --severity-threshold=medium. Dependency and container scans used
--ignore-policy; source scans used --include-ignores.
No suppression or license acceptance was added.
The snapshot starts at local commit 596c0f4803be1b34ea3404c3473d11e9bd9fd08f
and includes 90 uncommitted changes in the four services and their shared
build inputs. The initial audit commit is 8d6fb448; a manifest hashes 4,494
source/build inputs. Later audit commits change scan setup and inventory only.
The original working checkout and running services were not changed by the scans.
Installed public package versions in ai-orchestrator match its frozen uv.lock;
three missing lock entries are platform-specific packages. Installed Node
package versions match their lockfiles after normalizing ElevenLabs' leading
v; optional/platform package entries absent on this host remain in the scan.
Async-agent has ranged production requirements, so an additional scan
reproduced its exact local installed package versions. Both that scan and
the fresh requirements resolution found zero Medium-or-higher dependency
security advisories.
Coverage limits¶
Ai-orchestrator's full image requires proprietary Krisp ZIP archives that are
present only locally. Its container scan therefore covers the unchanged
runtime-base stage, not the final image, PortAudio runtime additions or
Krisp binaries. Its public Python dependency graph and source were scanned;
the proprietary runtime still needs a complete image/vendor assessment.
Images were rebuilt from the snapshot Dockerfiles and current registry bases. These are build assessments, not scans of live official Dev/QA containers. Static findings have not all been proven exploitable. In particular, existing URL guards and intentionally public JWT constants produce review candidates.
Reproducible evidence¶
The private audit branch is audit/core-snyk-local-20260917. It is an assessment
snapshot containing local work in progress and must not be merged as a feature PR.
| Evidence | GitHub Actions run |
|---|---|
| Container builds and initial source scans | 35268963279 |
| Successful source/dependency scans for all four services | 35269518225 |
| Exact locally installed async-agent dependencies | 35269841811 |
The first run failed dependency setup for three services; those failures are
not counted as clean scans. The successful retry supplied the source/dependency
results above. Run 35269208930 was canceled while queued and supplies no evidence.
JSON, SARIF, scanner versions, image metadata and input hashes are retained in
the private Actions artifacts for 14 days.
Fix plan¶
The version targets below come from the scan's fixed-version data. They are compatibility candidates, not upgrades already tested against the applications. Use separate PRs for each workstream so an unrelated API migration cannot hold up an available security patch.
1. Patch Critical runtime dependencies¶
| Service | Current package | Candidate fix | Implementation and verification |
|---|---|---|---|
| Both frontends | Next.js 16.2.9 | 16.3.3 or later patched 16.x | Update Next.js and matching lint config, regenerate locks, run typecheck/unit tests, build standalone images and test login/embed/session flows |
| Both frontends | undici 7.28.0 | 7.29.1 or later patched 7.x | Update both direct requirements and overrides; verify certificate rejection, outbound HTTP and isomorphic-dompurify paths |
| ai-manager | proxy-addr 2.0.7 through MCP SDK → Express | 2.0.8 | Refresh the transitive lock entry or use a scoped compatible override; test MCP authentication and proxy-address handling |
| ai-orchestrator | google-adk 1.32.0 | 1.38.0 within the existing <2 range |
Raise the direct floor and regenerate uv.lock; test ADK/A2A integration and tool authorization |
| ai-orchestrator | sqlparse 0.5.5 through Google Spanner/ADK | 0.6.0 | Resolve with the ADK/Spanner dependency family; verify compatibility rather than forcing an incompatible override |
These address Next.js traversal/optimization advisories, undici certificate validation, proxy-addr impersonation, ADK authorization and sqlparse code injection. Advisory IDs and all paths are in the CSV.
2. Patch the Debian bases¶
Apply the shared-base upgrade approach from PR #705 to the local async-agent Dockerfile and ai-orchestrator's runtime base, then rebuild with fresh layers.
| Package | Reported fixed Debian 13 version |
|---|---|
| perl-base | 5.40.1-6+deb13u1 |
| libsqlite3-0 | 3.46.1-7+deb13u2 |
| libpcre2-8-0 | 10.46-1~deb13u2 |
| gzip | 1.13-1+deb13u1 |
These upgrades cover 13 of the 17 Medium-or-higher OS advisories on each scanned Debian image. Four remain without a fixed Debian 13 package in the scan: zlib, attr, and two acl advisories, comprising three High and one Medium. Keep those visible and track upstream releases; do not remove required libraries or mix Debian unstable packages into the runtime to change a count. Obtain the Krisp build inputs through the existing vendor process and scan ai-orchestrator's complete final image before closing its container workstream.
3. Close source trust-boundary risks¶
| Priority | Finding group and evidence | Proposed change and acceptance check |
|---|---|---|
| First | surface-app SSRF: proxy.ts constructs the session-check destination from forwarded headers and sends cookies to it | Use a fixed trusted internal origin or share the session verifier directly. Test hostile host/protocol headers and redirects; cookies must never be sent to an arbitrary destination |
| First | ai-orchestrator's four Code Injection traces reach python_script_executor.py, which compiles and executes stored Python in the service process | Route these legacy tools through code-executor with the existing sandbox limits and explicit integration grants. Preserve tool outputs and prove user code cannot access the orchestrator process's environment or files |
| Next | surface-app's three JWT-decode findings, especially the reentry decision in session exchange | Verify existing cookie JWT signatures/issuer/audience/expiry before using them to authorize reentry. Test forged cookies and replayed bootstrap tokens; keep display-only decoding separate |
| Next | DOM XSS, unsafe redirects and postMessage findings across both frontends, including the embed SDK | Validate URL scheme and destination, constrain return paths, enforce exact message origins/source windows and review public diagnostic HTML. Test javascript/data URLs, external redirect targets and unrelated message senders |
| Next | ai-manager prototype pollution and regex/SQL traces in tool editing and authoring helpers | Trace each reported sink, reject prototype keys in object-path writes, retain parameterized SQL and bound regex work. Add regression tests for the actual sink rather than only its caller |
| Review | Cloud/MCP SSRF findings in ai-manager and ai-orchestrator | Existing region validators, safeFetch and initial URL guards must be included in the assessment. Test redirect hops, DNS rebinding and credential forwarding before deciding whether each finding needs code or a scanner review |
The scanner rates the stored-Python execution traces Medium. Their location inside a shared runtime is why this plan places the sandbox boundary ahead of many dependency Mediums; the plan does not claim a demonstrated production exploit.
4. Refresh remaining High/Medium dependencies¶
| Workstream | Main target families | Constraints to preserve |
|---|---|---|
| ai-orchestrator | aiohttp ≥3.14.3, cryptography ≥50.0.0, Pillow ≥12.3.0, pyasn1 ≥0.6.4, MCP ≥1.28.1, json-repair ≥0.60.1, httplib2 ≥0.32.0; Authlib, cert/HTTP helpers and parsing utilities | Update the existing security floors and uv.lock together. Check the MCP API generation before allowing a major upgrade. Remove the exact python-dotenv 1.1.1 pin in favor of a patched compatible release |
| Both frontends | DOMPurify ≥3.4.13, Mermaid ≥11.16.1, PostCSS ≥8.5.23, sharp ≥0.35.4, js-yaml ≥4.3.2, next-intl ≥4.9.2; nanoid, AJV and matching fast-uri versions | Cover nested copies and existing overrides. Test Markdown/SVG sanitization, image processing and schema validation |
| ai-manager | Hono ≥4.13.5, @hono/node-server ≥1.19.17, proxy-addr, qs, body-parser, browserslist and ts-deepmerge | Replace stale override floors, including the exact brace-expansion 2.1.2 override. Treat ts-deepmerge's major update as an API change |
| surface-app tooling | Vite/Vitest/esbuild, flatted, Rollup, tar, picomatch and the older AI SDK/provider-utils tree | Upgrade compatible parent packages together. The critical esbuild/flatted/Vitest findings are development-only paths; also cover the High @vitest/mocker advisory rather than stopping at the first patched Vitest version |
Surface-app's AI SDK 5/provider-utils 3 tree needs a compatible family upgrade: one provider-utils advisory only lists patched 4.x/5.x versions. Do not force a different provider-utils major under callers that expect 3.x. The CSV carries the remaining advisory-specific minima, including packages absent from this table. NLTK has a Medium advisory with no fixed version in the scan; assess its reachable use and track upstream separately.
For each dependency PR, run the affected service's lint/typecheck and tests, rebuild its production image, and rerun all applicable Snyk scans at Medium. Preserve the project-specific lockfile/install strategy. New overrides need a reason and a removal condition; avoid one blanket upgrade across four runtimes.
5. Review false positives and license notices separately¶
Several “hardcoded secret” findings point to public JWT issuer/audience names
or redaction masks, not credentials: the constants in
ai-manager/embed-jwt.ts,
surface-app/session-cookie.ts
and the tenant settings SECRET_MASK exports are examples.
Do not rotate public identifiers or weaken masking to silence these.
Review test fixtures and every remaining reported location individually.
Async-agent's SQL Injection finding
4ccb1c24-3c6b-4de7-9d71-ca9c04ffa00d follows authentication and database
results into remote tool dispatch. Reviewed queries bind $1/$2; the
sandbox payload is JSON. The PR rerun reports the same finding at
resolve_and_execute_mcp_tool, whose lookup also uses bound UUID parameters.
It remains open pending Security/Snyk false-positive review. Local .snyk
issue ignores do not resolve Snyk Code findings.
Handle the 24 license-policy notices as a separate approval/replacement task, including ai-orchestrator's GPL/LGPL and unknown-license reports. They do not substitute for or reduce the security remediation work.
PR #705 outcome¶
PR #705 now
includes commits 6b208feb and beb9d3c4. The patch upgrades inherited Debian
packages, names the sandbox HTTP transport explicitly, validates tenant UUIDs
at authentication and adds security regression coverage.
Validation: 177 async-agent tests passed, targeted Python lint passed, and the production image built and imported successfully as UID 10001. The verified Snyk rerun reports zero Critical and three High container findings, down from three Critical and ten High. Its dependency security scan is clean. The three container findings have no fixed Debian 13 package; the one High SQL source finding remains for review. No alert has been suppressed. This PR patch is separate from the unchanged local baseline above.