[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[security] fix: contain run artifact paths - #21

Open
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment
Open

[security] fix: contain run artifact paths#21
Hinotoi-agent wants to merge 2 commits into
agentenv:masterfrom
Hinotoi-agent:fix/artifact-path-containment

Conversation

@Hinotoi-agent

@Hinotoi-agentHinotoi-agent commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR hardens AgentFlow's run artifact filesystem boundary.

  • Rejects traversal-style run_id, node_id, and artifact name path segments before reading or writing artifacts.
  • Returns a bounded 400 response for invalid artifact API paths instead of resolving attacker-controlled .. segments.
  • Adds regression coverage for API reads and store write/read sinks.

Security issues covered

IssueImpactSeverity
Artifact API path traversalUntrusted path parameters could read files outside a node's artifact directory and, through store write sinks, write artifacts outside the intended run treeHigh

Before this PR

  • /api/runs/{run_id}/artifacts/{node_id}/{name} passed path parameters directly into RunStore.read_artifact_text(...).
  • RunStore built filesystem paths with raw run_id, node_id, and artifact name values.
  • URL-encoded .. path segments could escape artifacts/<node_id>/ and reach sibling files such as run.json or files outside the configured runs directory.
  • Artifact write helpers used the same path construction pattern.

After this PR

  • RunStore validates each path component as a single safe segment before constructing filesystem paths.
  • Empty values, ., .., /, and \ are rejected.
  • The artifact API maps invalid path segments to 400 invalid artifact path.
  • Regression tests cover traversal attempts through both the public API and store read/write helpers.

Why this matters

Run artifacts can contain prompts, stdout/stderr logs, launch metadata, result data, and other local execution details. A browser/API caller should only be able to read the requested node artifact, not escape into sibling run metadata or adjacent files.

Attack flow

encoded ../ path segment in artifact URL
-> FastAPI path parameters
-> RunStore path concatenation
-> read file outside intended artifact directory

Affected code

IssueFiles
Artifact path traversalagentflow/app.py, agentflow/store.py, tests/test_api.py

Root cause

Artifact path traversal:

  • Direct cause: artifact path components were concatenated into Path objects without checking that each component was a safe basename.
  • Boundary failure: public API path parameters were treated as trusted filesystem path components.

CVSS assessment

IssueCVSS v3.1Vector
Artifact API path traversal7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Rationale:

  • The public web API can be reached without application-layer authentication in the current app model, and successful traversal can disclose local run metadata or adjacent files readable by the AgentFlow process.

Safe reproduction steps

  1. Start an affected AgentFlow web API with a writable runs directory.
  2. Create or identify a run ID.
  3. Request an encoded traversal path such as:
    GET /api/runs/<run_id>/artifacts/%2E%2E/run.json
    
  4. Observe the pre-patch server returning the run metadata file instead of rejecting the path.
  5. Repeat after this PR and observe 400 invalid artifact path.

Expected vulnerable behavior

  • Pre-patch: encoded .. segments could escape the intended artifact directory.
  • Post-patch: traversal-like path segments are rejected before filesystem access.

Changes in this PR

  • Adds _safe_path_segment(...) in RunStore.
  • Applies segment validation to run_dir, node_artifact_dir, and artifact_path.
  • Handles invalid artifact API paths with HTTP 400.
  • Adds tests for API traversal rejection and store-level read/write traversal rejection.

Files changed

CategoryFilesWhat changed
Store hardeningagentflow/store.pyValidate path components before building run/artifact paths
API hardeningagentflow/app.pyReturn a bounded client error for invalid artifact paths
Teststests/test_api.pyAdd traversal regression tests for API and store sinks

Maintainer impact

  • The patch is narrow and only affects path component validation for run/artifact storage helpers.
  • Legitimate generated run IDs, node IDs, and fixed artifact filenames continue to work.
  • Invalid traversal-style paths fail before filesystem access.

Fix rationale

Filesystem boundaries should be enforced at the storage helper layer, not only at the route layer, because the same helper is used by both read and write paths. Validating each component as a basename gives a simple invariant that is hard to bypass during future refactors.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Targeted traversal regression tests pass.
  • API/store regression tests pass.
  • Full test suite completed locally.

Executed with:

  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py::test_api_rejects_artifact_path_traversal tests/test_api.py::test_store_rejects_artifact_write_path_traversal tests/test_api.py::test_store_rejects_artifact_read_path_traversal -q
  • /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest tests/test_api.py tests/test_store_and_validation.py -q

Full suite note:

  • I also attempted /Users/lennon/.hermes/hermes-agent/venv/bin/python3.11 -m pytest -q; it did not complete within 600s in my local environment and showed broad unrelated failures before timeout, so the focused API/store checks above are the validation for this patch.

Disclosure notes

  • This PR is bounded to artifact path containment.
  • It does not include unrelated authentication or execution-policy changes.
  • No unrelated files were changed.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens AgentFlow’s run artifact filesystem boundary by validating run_id, node_id, and artifact name as safe single path segments before constructing paths, preventing traversal-style reads/writes outside the intended run tree.

Changes:

  • Added _safe_path_segment(...) validation and applied it to run/artifact path construction in RunStore.
  • Updated the artifact read API to return HTTP 400 (invalid artifact path) when store path validation fails.
  • Added regression tests covering traversal attempts via the API and store read/write helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
agentflow/store.pyIntroduces _safe_path_segment and enforces safe basename-only segments for run/node/artifact filesystem paths.
agentflow/app.pyMaps store ValueError from invalid artifact path segments to a bounded HTTP 400 response.
tests/test_api.pyAdds regression tests for traversal rejection through both the HTTP API and direct store helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadagentflow/store.py
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Hinotoi-agent