Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

bug: POST/PUT bodies are silently truncated at the first space

Open
#44 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
67/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
cli

Research direction

Start with build_request_parts in git_api/cli.py and trace how the REPL passes the raw command line through shlex.split; read tests/test_cli.py, especially test_repl_post_substitutes_variables_in_body. Add coverage for JSON and space-separated bodies, then verify the body arrives intact, including quotes and all text after the URL.

Written by the indexing model from the issue text.

Description

bug

Summary

GET/POST/PUT bodies are taken from parts[2] only, so any body containing a space is silently truncated — everything after the first space is dropped. shlex.split() has already shredded the rest, and for JSON bodies it also strips every " quote character, so the API receives a body that is not the JSON the user typed.

Steps to reproduce

Start the REPL in an empty directory, set a base_url, then send a normal JSON body:

mkdir /tmp/gapi-demo && cd /tmp/gapi-demo
git init -q
git-api

Inside the REPL:

var base_url=http://127.0.0.1:8000
POST /users {"name":"Yunare Maia","active":true}

The request never needs to be a real API — build_request_parts produces the body before any socket is opened, so this reproduces with no server at all.

Actual output

Direct call against the real code:

input line : POST /users {"name":"Yunare Maia","active":true}
shlex.split: ['POST', '/users', '{name:Yunare Maia,active:true}']
url        : https://api.example.com/users
body sent  : '{name:Yunare Maia,active:true}'

expected body: '{"name":"Yunare Maia","active":true}'
TRUNCATED?  True

The truncation is worse for bodies where the space-separated tokens do not happen to be inside quotes:

line  : POST /users {"a": 1, "b": 2}
parts : ['POST', '/users', '{a:', '1,', 'b:', '2}']
BODY  : '{a:'
rest after parts[2] DROPPED: ['1,', 'b:', '2}']

line  : PUT /users/1 name=Yunare role=admin
parts : ['PUT', '/users/1', 'name=Yunare', 'role=admin']
BODY  : 'name=Yunare'
rest after parts[2] DROPPED: ['role=admin']

End-to-end through the real REPL binary, with a local server echoing back the exact bytes it received:

========== REPL stdout (verbatim) ==========
🌿  Git-API REPL 0.1.0
    Type 'help' for commands, 'quit' to exit.

default> Set base_url = http://127.0.0.1:39881
default>
→  POST http://127.0.0.1:39881/users
←  200
{
  "ok": true
}

  (use 'save post--users' to save)
default> Bye.
--- exit code: 0 ---
========== SERVER RECEIVED ==========
[
  {
    "method": "POST",
    "path": "/users",
    "body": "{name:Yunare Maia,active:true}"
  }
]

The server received {name:Yunare Maia,active:true}. The " characters are gone and the payload is not valid JSON — yet the REPL printed a green ← 200 and gave no warning that anything was dropped.

Expected output

The body should arrive exactly as typed. Two things need fixing:

  1. Everything after the URL is the body, not just the third token:

    raw_body = " ".join(parts[2:]) if len(parts) > 2 else None
    
  2. A JSON body must survive shell-style quote stripping. shlex.split treats " as a quoting character, so {"name":"x"} loses them. Either parse the body out of the raw line before shlex.split (e.g. split on the first two whitespace runs), or read JSON bodies from a variable / saved request.

Worth noting that PUT /users/1 name=Yunare role=admin losing role=admin is the more dangerous variant of this bug: the request still succeeds, and the field that silently vanished is the one the user cared about.

Root cause

git_api/cli.py:140

raw_body = parts[2] if len(parts) > 2 else None

parts is the output of shlex.split(line) (cli.py:323). shlex.split tokenises on whitespace and strips quotes, so a body with spaces becomes many tokens and parts[2] keeps only the first. parts[3:] is never read, so the remainder is discarded without any diagnostic.

The help text at cli.py:249-250 advertises POST <url> [body], which implies the body is the rest of the line, and README.md shows POST as a first-class command — so this is the documented path, not an edge case.

There is no test covering a multi-token body. tests/test_cli.py:397 (test_repl_post_substitutes_variables_in_body) uses a single-token body, which is why the suite passes.

Suggested fix

--- a/git_api/cli.py
+++ b/git_api/cli.py
@@ def build_request_parts(parts: list[str], env: dict):
     url = resolve_url(parts[1], env)
-    raw_body = parts[2] if len(parts) > 2 else None
+    # The body is the rest of the line, not just the third token.
+    raw_body = " ".join(parts[2:]) if len(parts) > 2 else None

Note this alone still loses the " characters, because shlex.split has already removed them. The complete fix needs the body extracted from the raw line, e.g.:

import re
_BODY_SPLIT = re.compile(r"\S+\s+\S+(?:\s+(.*))?\s*$")

def split_command(line: str) -> list[str]:
    """Split into command + url + the untouched remainder as the body."""
    parts = shlex.split(line)
    if len(parts) > 2:
        m = _BODY_SPLIT.match(line)
        parts[2:] = [m.group(1)] if m and m.group(1) else []
    return parts

Related

  • Related to the {{base_url}} double-prefix defect: same build_request_parts function, same parse-then-resolve ordering.
  • #34 (closed) hardened execute_request against bad input, but nothing guards body parsing.
Dominant language
Python
Stars
0
Forks
1
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.