Skip to content

release: 3.37.0#59

Merged
stainless-app[bot] merged 3 commits intomainfrom
release-please--branches--main--changes--next
Apr 28, 2026
Merged

release: 3.37.0#59
stainless-app[bot] merged 3 commits intomainfrom
release-please--branches--main--changes--next

Conversation

@stainless-app
Copy link
Copy Markdown
Contributor

@stainless-app stainless-app Bot commented Apr 28, 2026

Automated Release PR

3.37.0 (2026-04-28)

Full Changelog: v3.36.0...v3.37.0

Features

  • support setting headers via env (9d86a3c)

Bug Fixes

  • use correct field name format for multipart file arrays (a94f7a1)

This pull request is managed by Stainless's GitHub App.

The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.

For a better experience, it is recommended to use either rebase-merge or squash-merge when merging this pull request.

🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions

Copy link
Copy Markdown

@vorflux vorflux Bot left a comment

Choose a reason for hiding this comment

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

Reviewed — found 2 issues in the new SUPERMEMORY_CUSTOM_HEADERS env-var parsing added to both Supermemory and AsyncSupermemory.


Review with Vorflux

Comment on lines +118 to +125
custom_headers_env = os.environ.get("SUPERMEMORY_CUSTOM_HEADERS")
if custom_headers_env is not None:
parsed: dict[str, str] = {}
for line in custom_headers_env.split("\n"):
colon = line.find(":")
if colon >= 0:
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[High] Env-supplied headers can silently override the SDK's Authorization header

parsed is placed at the front of the merge ({**parsed, **(default_headers or {})}), but default_headers are applied before auth_headers in the base client. This means setting SUPERMEMORY_CUSTOM_HEADERS='Authorization: Bearer evil' will replace the bearer token derived from api_key, redirecting all requests to the wrong credentials without any warning.

Consider stripping or blocking reserved headers (Authorization, X-Api-Key, etc.) from parsed before merging, or placing parsed after auth_headers in the resolution order so auth always wins.

for line in custom_headers_env.split("\n"):
colon = line.find(":")
if colon >= 0:
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Medium] Malformed header lines produce empty header names instead of failing fast

A line like ': bad' passes the colon >= 0 check and inserts "" (empty string) as a header name. This doesn't raise at construction time — it propagates into the request and causes a generic runtime error later, making the root cause hard to diagnose.

Add a guard to skip (or raise) when the header name is empty after stripping:

name = line[:colon].strip()
if not name:
    continue  # or raise ValueError(f"Invalid header line: {line!r}")
parsed[name] = line[colon + 1:].strip()

Comment on lines +453 to +460
custom_headers_env = os.environ.get("SUPERMEMORY_CUSTOM_HEADERS")
if custom_headers_env is not None:
parsed: dict[str, str] = {}
for line in custom_headers_env.split("\n"):
colon = line.find(":")
if colon >= 0:
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[High] Same Authorization override issue as in Supermemory (sync client)

Identical logic in AsyncSupermemory.__init__SUPERMEMORY_CUSTOM_HEADERS can silently replace the bearer token. Same fix applies: block reserved auth headers or ensure auth_headers always takes precedence.

for line in custom_headers_env.split("\n"):
colon = line.find(":")
if colon >= 0:
parsed[line[:colon].strip()] = line[colon + 1 :].strip()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Medium] Same empty header name issue in AsyncSupermemory

Identical to the sync client: malformed lines (e.g. ': bad') produce an empty header name that propagates silently into requests. Same guard needed here.

@stainless-app stainless-app Bot merged commit ec6073a into main Apr 28, 2026
9 checks passed
@stainless-app
Copy link
Copy Markdown
Contributor Author

stainless-app Bot commented Apr 28, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants