Skip to content

fix(security): validate Postman collection/environment ids, block path transversal - #1158

Open
FabioLeitao wants to merge 1 commit into
usestrix:mainfrom
FabioLeitao:fix/postman-collection-uid-path-traversal
Open

fix(security): validate Postman collection/environment ids, block path transversal#1158
FabioLeitao wants to merge 1 commit into
usestrix:mainfrom
FabioLeitao:fix/postman-collection-uid-path-traversal

Conversation

@FabioLeitao

Copy link
Copy Markdown

Validate Postman collection/environment ids, block path traversal

postman:// targets took collection_uid/environment_uid as raw netloc+path with no format check. requests collapses ".." in a URL path before sending the request, so fetch_postman_collection/fetch_postman_environment built {POSTMAN_API_BASE}/collections/{uid} (and /environments/{uid}) with an attacker-controlled string that could traverse to an arbitrary Postman API endpoint -- authenticated with the caller's own POSTMAN_API_KEY via X-Api-Key.

Confirmed with requests.Request(...).prepare().url:
'https://api.getpostman.com/collections/x/../../workspaces'
-> 'https://api.getpostman.com/workspaces'

Also: a successfully-fetched collection was written with the default umask (world-readable on a shared host) even though it can carry saved auth headers/tokens, while this codebase already has write_secret_text (0600) for exactly this kind of data in three other call sites.

Fix:

  • validate_postman_uid() (letters/digits/hyphens only, matching real Postman uid shape) enforced both at target-parse time (fail fast, clear error) and inside fetch_postman_collection/fetch_postman_environment (defense in depth for any other caller).
  • write_fetched_collection now uses write_secret_text instead of Path.write_text.

Full test suite: 1058 passed. mypy/ruff clean on touched files. 6 new tests cover the traversal rejection (asserting requests.get is never called), an @-authority variant, and the file-permission fix.

…h traversal

postman:// targets took collection_uid/environment_uid as raw netloc+path with
no format check. requests collapses ".." in a URL path before sending the
request, so fetch_postman_collection/fetch_postman_environment built
`{POSTMAN_API_BASE}/collections/{uid}` (and /environments/{uid}) with an
attacker-controlled string that could traverse to an arbitrary Postman API
endpoint -- authenticated with the caller's own POSTMAN_API_KEY via X-Api-Key.

Confirmed with requests.Request(...).prepare().url:
  'https://api.getpostman.com/collections/x/../../workspaces'
  -> 'https://api.getpostman.com/workspaces'

Also: a successfully-fetched collection was written with the default umask
(world-readable on a shared host) even though it can carry saved auth
headers/tokens, while this codebase already has write_secret_text (0600) for
exactly this kind of data in three other call sites.

Fix:
- validate_postman_uid() (letters/digits/hyphens only, matching real Postman
  uid shape) enforced both at target-parse time (fail fast, clear error) and
  inside fetch_postman_collection/fetch_postman_environment (defense in depth
  for any other caller).
- write_fetched_collection now uses write_secret_text instead of
  Path.write_text.

Full test suite: 1058 passed. mypy/ruff clean on touched files. 6 new tests
cover the traversal rejection (asserting requests.get is never called),
an @-authority variant, and the file-permission fix.
@FabioLeitao

Copy link
Copy Markdown
Author

Human-Reviewed-By: @FabioLeitao (#1158)

-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgHSwaWVCd3rALjtmwINNtVKRX3t
ucF7TfXNZFALyRo4EAAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
OQAAAEA37LsnRBusiPsLZJHj3h2g25EvSgUCeUtWLUdiD3DhVuBw7g3z37sMw2Ns/3LbOd
4EWQxtntM5YZV2U5PHX/cL
-----END SSH SIGNATURE-----

@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR hardens Postman target handling and protects fetched collection contents.

  • Validates collection and environment UIDs before authenticated Postman API requests, preventing path traversal to unrelated endpoints.
  • Applies validation during target parsing and again in the fetch helpers for defense in depth.
  • Writes fetched collections with restricted permissions through the existing secret-file helper.
  • Adds regression coverage for traversal inputs, authority-like inputs, suppressed network requests, and file permissions.

Confidence Score: 5/5

The PR appears safe to merge, with no actionable regressions identified in the changed paths.

The new validation accepts the documented Postman UID forms while blocking path separators and other unsafe characters, and the secret-file writer preserves required directory creation and overwrite behavior while improving permissions and symlink safety.

Important Files Changed

Filename Overview
strix/interface/utils.py Adds early Postman UID validation and securely writes fetched collections without disrupting parent-directory creation or staging behavior.
strix/utils/api_spec.py Introduces a strict Postman UID allowlist and enforces it before collection and environment API requests.
tests/test_api_spec.py Verifies malformed UIDs are rejected before any authenticated network request occurs.
tests/test_api_spec_targets.py Covers target-level traversal and authority rejection along with restricted fetched-file permissions.

Reviews (1): Last reviewed commit: "fix(security): validate Postman collecti..." | Re-trigger Greptile

Sign up for free to 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.

1 participant