Skip to content

Releases: Jovancoding/Network-AI

v5.10.2 — Security patch: CodeQL #174 CWE-377 root cause fix

08 Jun 21:46

Choose a tag to compare

Security Patch Release

v5.10.2 resolves CodeQL alert #174 (CWE-377 Insecure Temporary File).

CodeQL #174 — CWE-377 Root Cause Fix ( est-claim-verifier.ts)

The v5.10.1 fix applied path.resolve() in the AuthGuardian constructor, but this does not satisfy CodeQL's taint analysis — the taint chain from os.tmpdir() through
esolve() into writeFile() remains intact.

The actual taint sources were the join(tmpdir(), ...) calls in est-claim-verifier.ts. All 10 occurrences have been replaced with join('.', 'data', ...) paths, eliminating the CWE-377 source entirely. AuthGuardian constructor retains path.resolve() for defense-in-depth.

50/50 claim verifier tests still pass.

Full changelog

See CHANGELOG.md.

v5.10.1 — Security patch: CodeQL #174 + SkillSpector findings

08 Jun 21:28

Choose a tag to compare

Security Patch Release

v5.10.1 resolves three security findings identified by CodeQL and SkillSpector.

CodeQL #174 — CWE-377 Insecure Temporary File Path (lib/auth-guardian.ts)

AuthGuardian stored rustConfigPath as-is from the caller. Tests supply os.tmpdir()-derived paths, which CodeQL flagged as an insecure temporary file taint flow into writeFile(). The constructor now calls path.resolve() on the supplied path, breaking the taint chain — the same pattern used to resolve #65#68 in v3.4.1.

SkillSpector Intent-Code Divergence — FILE_EXPORT missing from HIGH_RISK_RESOURCES (scripts/check_permission.py)

The inline comment and SKILL.md security policy both stated that FILE_EXPORT requires --confirm-high-risk, but HIGH_RISK_RESOURCES only contained PAYMENTS and DATABASE. File export requests could receive advisory grants without the extra acknowledgment step. FILE_EXPORT is now included in the set.

SkillSpector Description-Behavior Mismatch — ensure_data_dir() ignoring env scope (scripts/check_permission.py)

ensure_data_dir() always created the fixed top-level data/ directory, ignoring NETWORK_AI_ENV. In a multi-environment deployment this caused audit log and grant files to be written to the wrong location when env-scoped paths were active, potentially mixing dev/test/prod state. The function now delegates to _resolve_data_dir() for correct env-scoped behavior.

Also in this patch

  • @types/node constraint corrected from ^25.10.0 (non-existent) to ^25.0.0; fixes CI ETARGET failure on
    pm ci (introduced in v5.10.0).
  • SKILL.md scan findings table updated with both SkillSpector resolved entries.

Full changelog

See CHANGELOG.md.

v5.10.0 — ClaimVerifier: Tier 1 Agent Honesty / Lie Detector

08 Jun 20:48

Choose a tag to compare

ClaimVerifier — Tier 1 Agent Honesty / Lie Detector

Network-AI v5.10.0 ships ClaimVerifier, a new subsystem that lets the orchestrator independently verify whether an agent's claimed actions actually happened — catching fabricated results, exaggerated outcomes, and undisclosed side-effects.

How it works

AgentRuntime now issues an HMAC-signed ExecutionReceipt for every exec() and writeFile() call, committing to { agentId, action, target, exitCode, outputHash }. The runtime — not the agent — is the sole outcome authority. Tampering with any field (including exitCode or outputHash) invalidates the signature.

ClaimVerifier.verify(manifests, agentId, windowMs) reconciles agent-declared ActionManifest[] against the in-memory RuntimeAuditEntry log:

  • UNSUPPORTED_CLAIM — agent declared an action that has no matching runtime witness (forged or imagined)
  • UNDISCLOSED_ACTION — runtime witnessed an action the agent did not declare

Both violation types surface through ComplianceMonitor and can trigger AuthGuardian trust decay.

Trust decay

AuthGuardian.recordClaimViolation() increments a per-agent counter on each UNSUPPORTED_CLAIM. After N consecutive violations (default 3) trust drops by 0.1. Below 0.4 trust, the agent is forced into ApprovalGate supervised execution.
esetClaimViolations() resets the counter on a corroborated turn.

New exports

ypescript import { ClaimVerifier, ActionManifest, VerificationOutcome, VerificationResult } from 'network-ai' import { ExecutionReceipt } from 'network-ai'

Scope (Tier 1)

ClaimVerifier operates on AgentRuntime-mediated actions only. Unmediated BYOC adapter network calls, per-session audit log boundaries, and the interpretive gap between "ran" and "succeeded semantically" are documented in THREAT_MODEL.md section 8 as Tier 2 hardening candidates (capability broker + process isolation + egress-deny).

Changes

  • New: lib/claim-verifier.ts — ClaimVerifier, ActionManifest, VerificationOutcome, VerificationResult, ClaimVerifierOptions
  • New: est-claim-verifier.ts — 50 tests (Phase 1: receipt, Phase 2: reconciliation, Phase 3: trust decay)
  • Modified: security.ts — ExecutionReceipt interface; SecureTokenManager.generateReceipt() / �alidateReceipt()
  • Modified: lib/agent-runtime.ts — ShellResult.receipt?, FileResult.receipt?, receipt emission after exec/write
  • Modified: lib/auth-guardian.ts —
    ecordClaimViolation(),
    esetClaimViolations(), getClaimViolationCount(), getTrustLevel()
  • Modified: lib/compliance-monitor.ts — ViolationType extended with UNSUPPORTED_CLAIM, UNDISCLOSED_ACTION
  • Modified: ypes/agent-adapter.d.ts — AgentResult.metadata.receipts?
  • Modified: index.ts — exports for all new types
  • Modified: THREAT_MODEL.md — section 8 Tier 1 scope ceiling + Tier 2 hardening path
  • Docs: README, SECURITY, .github/SECURITY, SKILL.md, CONTRIBUTING, AUDIT_LOG_SCHEMA, CHANGELOG, ARCHITECTURE — all updated for v5.10.0

Test suite

3211 tests across 32 suites (50 new + 3161 existing), 0 failures.

Full changelog

See CHANGELOG.md.

v5.9.1 — Critical: Shell Command Injection Fix (GHSA-qw6v-5fcf-5666)

02 Jun 20:35

Choose a tag to compare

Network-AI v5.9.1 — Critical Security Patch

🔒 Security — GHSA-qw6v-5fcf-5666 (Critical, CWE-78 OS Command Injection)

SandboxPolicy.isCommandAllowed glob-matched the entire command string, but ShellExecutor then ran that string through /bin/sh -c (or cmd.exe /c). A scoped allowlist entry such as git *, npm *, or node * therefore also matched chained payloads like git status; id, and the injected command executed — defeating the one control the threat model designates against a compromised agent (Adversary 3.2).

Fixed:

  • Commands now execute via spawn(file, args, { shell: false }) using a parsed argv — no shell is ever invoked, so metacharacters cannot be interpreted.
  • A new quote-aware parseCommandLine() tokenizer backs both isCommandAllowed() and the new SandboxPolicy.tokenizeCommand().
  • Any unquoted shell metacharacter (; & | $ ` ( ) < > { } newline) or unterminated quote is rejected before the allowlist glob match.
  • Quoted metacharacters are preserved as literal argument data.

Reported by lexdotdev.

🛠 Fixed

  • scripts/check_permission.py — permission_denied audit logging: audit_summary reads explicit permission_denied events (v5.9.0), but the permission checker never wrote them. A new _deny() helper now logs a permission_denied audit event (agent_id, resource_type, scope, reason, scores) at every denial point — high-risk confirmation, insufficient justification, low trust, excessive risk, below-threshold weighted score.
  • Socket.dev Network-access false positive — declared lib/telemetry-provider.ts / dist/lib/telemetry-provider.js in socket.json. The module defines the BYOT ITelemetryProvider interface and createOtelHooks() factory and makes no outbound HTTP calls.
  • Tests — added command-injection regression coverage (chaining, pipe, $(), backticks, redirection, newline, quoted-literal handling, tokenizeCommand); converted shell-builtin test commands to node -e since execution is now shell-free.

✅ Verification

  • npx tsc --noEmit — clean
  • Full suite — 3,161 tests across 31 suites passing

Upgrade urgency: HIGH for any deployment that grants agents ShellExecutor access with a scoped allowlist.

v5.9.0 — SkillSpector audit_summary explicit denial counts

01 Jun 18:17

Choose a tag to compare

What's Changed

Fixed

  • SkillSpector Intent-Code Divergence (88%) — audit_summary in scripts/check_permission.py: Denial counts were inferred as total_requests - total_grants instead of being read from explicit permission_denied audit log entries. The AuthGuardian engine already logs a permission_denied event for every rejected request — the script simply ignored them. Denial counts now accumulated directly from action == "permission_denied" entries, per-agent and per-resource. JSON output now includes "denial_source": "explicit_permission_denied_events" for auditability. Docstring updated to match real behavior.
  • SKILL.md ASI01 finding description: Updated to reflect that the scope guard now responds directly for simple requests instead of always decomposing.

Full Changelog: v5.8.9...v5.9.0

v5.8.9 — CodeQL #170/#173 taint-break + BOM fix + prompt cleanup

30 May 21:49

Choose a tag to compare

What's Changed

Fixed

  • CodeQL #170 — CWE-367 TOCTOU (test-phase11.ts stale-lock inject): lockPath tainted via new FileLock(lockPath) internal existsSyncopenSync(lockPath, 'w'). Fixed with fresh const staleLockPath = join(dir, '.test.lock') inside the write block.
  • CodeQL #173 — CWE-367 TOCTOU (test-phase11.ts orphan-tmp simulate): tmpPath flowed from assert(!existsSync(tmpPath)) into openSync(tmpPath, O_CREAT|O_EXCL|O_WRONLY). Fixed with fresh const orphanTmpPath inside the write block.
  • UTF-8 BOM regression: PowerShell 5.1 Set-Content writes BOM, breaking ts-node JSON parse in CI. All version-bump scripts now use System.IO.File::WriteAllText with UTF8Encoding(false).
  • claude-project-prompt.md residual hardcoded-3 refs: Pre-commit checklist and response-format template still referenced "3 sub-tasks" after v5.8.8 SkillSpector fix. Both updated to be count-agnostic.

Full Changelog: v5.8.8...v5.8.9

v5.8.8 — CodeQL TOCTOU data-flow fix + SkillSpector NLP guard

30 May 20:12

Choose a tag to compare

What's Changed

Fixed

  • CodeQL #169–#171 — CWE-367 TOCTOU data-flow break ( est-phase11.ts): openSync(lockPath/tmpPath, 'w') write blocks still triggered js/file-system-race because CodeQL traced the same variables from earlier existsSync assertions. Fixed #169–#170 by replacing existsSync(lockPath) assertions with lock.getStatus().locked (breaks the taint chain at the check side). Fixed #171 by switching the .tmp orphan-simulation write to O_CREAT|O_EXCL|O_WRONLY — atomic-create is the CodeQL-recommended pattern and correct here since the file must not already exist.
  • CodeQL #172 — unused writeFileSync import ( est-phase11.ts): All three path-based writes were replaced with fd operations in v5.8.7; the now-unused import is removed. constants and unlinkSync added in its place.
  • SkillSpector Natural-Language Policy Violations (71%) (claude-project-prompt.md): "DECOMPOSE every complex request into exactly 3 sub-tasks" was unconditional, forcing sub-agent orchestration for all requests. Added a scope guard so the decomposition protocol only applies to genuinely complex, multi-domain requests.
  • Test noise — red stderr warnings: NETWORK_AI_MINIMAL=1 now scoped to estAtomicSnapshot/ estPriorityEviction only (set on entry, deleted in inally), silencing expected disableWal warnings without affecting Feature 2's real WAL replay coverage.

Full Changelog: v5.8.7...v5.8.8

v5.8.7 — CodeQL CWE-367 fixes + SkillSpector comment fix

30 May 18:01

Choose a tag to compare

What's changed

Fixed — CodeQL alerts #165–#168

#165, #166, #167 — CWE-367 TOCTOU (test-phase11.ts)
Three writeFileSync(path, data) calls in the new testLockOwnership() and testAtomicSnapshot() test helpers were flagged as potential file-system race conditions (js/file-system-race). The path-then-write pattern has a window where the file could change between resolution and the write. Replaced all three with fd-based writes (openSyncwriteSynccloseSync), consistent with how production code in lib/locked-blackboard.ts handles the same pattern.

#168 — Unused variable staleRelease (test-phase11.ts)
The return value of lock2.release() was assigned to staleRelease but never read. Removed the assignment; the existsSync assertion that follows is the actual correctness check.

Fixed — SkillSpector Intent-Code Divergence (94% confidence)

scripts/blackboard.py--path scope comment
The header comment described --path as "accepted for environment routing" and "validated against the project root", which SkillSpector flagged because it implies full state isolation. In reality, only the main blackboard file path is derived from --path; lock files and pending-change files always resolve from the global data/ directory. The comment has been rewritten to accurately state the actual scope, preventing operator confusion in multi-project environments.


Full changelog: https://github.com/Jovancoding/Network-AI/blob/main/CHANGELOG.md

v5.8.6 — LockedBlackboard correctness fixes

30 May 17:35

Choose a tag to compare

What's changed

Fixed — lib/locked-blackboard.ts (5 correctness fixes)

#1 — Stale-lock compare-and-delete race (acquire())
Previously, two concurrent waiters that both observed a stale lock would each call forceRelease() (blind unlink). The one that lost the race would then delete the fresh lock acquired by the winner. Fixed with forceReleaseStale(expectedAcquiredAt, expectedPid): re-reads the lock file and only unlinks it if the identity still matches, so a freshly-acquired lock is never deleted.

#2 — Ownership-blind release() unlink
release() checked the in-memory lockHolder string but then unconditionally called unlinkSync. If another process had already force-released our stale lock and created its own, we would delete theirs. Fixed: release() reads the lock file after closing the fd and verifies holder + pid before unlinking.

#3 — Non-atomic snapshot write
persistToDiskInternal() and writeInitialBlackboard() wrote directly to the final blackboard path. A process crash mid-write (after WAL compaction) would leave a truncated file with no WAL to recover from. Fixed: both functions now write to ${path}.tmp then renameSync to the final path — rename is atomic on local POSIX/NTFS.

#4 — WAL/pending reconciliation — zombie validated entries
After WAL replay writes a key to the cache, loadPendingChanges() could re-add the validated pending file for the same change (crash occurred before archivePendingChange ran). Result: a zombie entry that always fails commit() with a hash-conflict. Fixed: loadPendingChanges() now cross-checks each validated entry against the cache after WAL replay and immediately archives entries whose hash already matches the committed state.

#5cleanupOldPendingChanges() priority-unaware eviction
Age-only eviction could discard high-priority proposals waiting at an approval gate. Fixed: sort by priority ASC then proposed_at ASC — lowest-priority and oldest entries are evicted first.

#11 — Silent disableWal in production
disableWal: true via constructor was completely silent. A stray NETWORK_AI_MINIMAL=1 in a production environment would silently disable crash recovery. Fixed: emits log.warn at startup when WAL is disabled outside the recognised CI/test env-var path.

Tests

Three new suites added to test-phase11.ts (55 assertions total, up from 43):

  • testLockOwnership() — 7 assertions: release-without-hold, acquire/release cycle, ownership-verified release does not delete a foreign lock, stale-lock cleanup allows fresh acquire
  • testAtomicSnapshot() — 3 assertions: no orphaned .tmp after successful write, state integrity, graceful load with a pre-existing orphaned .tmp
  • testPriorityEviction() — 2 assertions: high-priority validated change survives a pending overflow eviction cycle, surviving change commits successfully

Documentation

  • ARCHITECTURE.md — WAL durability scope clarified: protects against process crashes only (no fsync, no power-loss guarantee); atomic tmp+rename described; NFS v2/v3 explicitly unsupported (O_EXCL non-atomic over NFS); disableWal/NETWORK_AI_MINIMAL usage scope documented
  • SECURITY.md / .github/SECURITY.md — new LockedBlackboard Mutex Correctness (v5.8.6) bullet summarising all five fixes
  • Test count updated to 3,148 across 31 suites (was 3,136)

Dependencies (via Dependabot)

  • openai bumped from 6.38.0 → 6.39.0 (#104)

Full changelog: https://github.com/Jovancoding/Network-AI/blob/main/CHANGELOG.md

v5.8.5 — Audit log justification data minimisation (Ssd3)

24 May 14:27

Choose a tag to compare

Network-AI v5.8.5 — Audit log justification data minimisation (Ssd3)

Security

Three related SkillSpector Ssd3 findings (98%/99%/99%) addressed in scripts/check_permission.py.

Justification truncation before audit log write (Ssd3, 99%)
Justification strings are now truncated to 200 characters before being written to audit_log.jsonl. Content beyond that limit is dropped and a [truncated] suffix is appended. The full in-memory string is still used for score_justification() scoring. A named constant _JUSTIFICATION_MAX_LOG_LEN = 200 controls the limit.

Justification redacted from audit summary JSON output (Ssd3, 99%)
--audit-summary --json previously included raw log entries in the recent array, creating a secondary retrieval path for earlier justification text. The justification key is now stripped from each entry's details dict in JSON output via an inline _redact_entry() helper. Human-readable (non-JSON) output is unaffected.

Header comment updated (Ssd3, 98%)
The script header now describes truncation and summary redaction rather than saying justifications are logged verbatim. SKILL.md privacy.audit_log.pii_warning updated to match.

Files changed

scripts/check_permission.py, SKILL.md, CHANGELOG.md, package.json, skill.json, openapi.yaml, README.md, and all version-bearing doc files.