Skip to content

fix: add buffer-length check in unzip.cpp - #429

Open
anupamme wants to merge 2 commits into
NativeScript:mainfrom
anupamme:fix-repo-ios-heap-buffer-overflow-unzip-strcpy
Open

fix: add buffer-length check in unzip.cpp#429
anupamme wants to merge 2 commits into
NativeScript:mainfrom
anupamme:fix-repo-ios-heap-buffer-overflow-unzip-strcpy

Conversation

@anupamme

@anupamme anupamme commented Aug 4, 2026

Copy link
Copy Markdown

Summary

Fix critical severity security issue in TKLiveSync/unzip.cpp.

Vulnerability

Field Value
ID V-001
Severity CRITICAL
Scanner multi_agent_ai
Rule V-001
File TKLiveSync/unzip.cpp:49
Assessment Likely exploitable
CWE CWE-120

Description: A PATH_MAX-sized heap buffer (pathcopy) receives ZIP entry names via strcpy() without bounds checking. ZIP specification allows entry names up to 65535 bytes, far exceeding typical PATH_MAX values (4096 or 1024). This creates a classic buffer overflow where crafted long filenames overflow the heap buffer.

Evidence

Exploitation scenario: Attacker creates a ZIP archive with an entry name longer than PATH_MAX bytes.

Scanner confirmation: multi_agent_ai rule V-001 flagged this pattern.

Production code: This file is in the production codebase, not test-only code.

Threat Model Context

This is a Node.js library - vulnerabilities affect downstream consumers who use this package.

Changes

  • TKLiveSync/unzip.cpp

Behavior Preservation

The change is scoped to 1 file on the vulnerable path; it only tightens handling of untrusted input and leaves valid inputs unaffected.


Automated security fix by OrbisAI Security

Summary by CodeRabbit

  • Bug Fixes

    • Improved ZIP extraction reliability by handling directory paths more safely.
    • Avoided unnecessary directory creation when ZIP entries do not include a directory path.
  • Refactor

    • Simplified internal path handling during archive extraction.

Automated security fix generated by OrbisAI Security
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The ZIP extraction code removes an unused header and temporary buffer. It extracts parent directories with std::string and creates directories only when ZIP entry paths contain a slash.

Changes

ZIP directory handling

Layer / File(s) Summary
Parent directory extraction and cleanup
TKLiveSync/unzip.cpp
The code removes the unused <libgen.h> dependency and pathcopy allocation. It extracts parent directories with std::string::find_last_of('/') and skips mkdir_rec for entries without a directory component.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

I’m a rabbit in the ZIP tonight,
Making parent paths neat and right.
No spare buffer hops around,
Only real directories touch the ground.
Slash or no slash, the rule is clear—
Clean extraction brings good cheer!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the buffer-safety fix in unzip.cpp, which matches the pull request objective despite the implementation replacing the buffer.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@NathanWalker

Copy link
Copy Markdown
Contributor

Thank you, target main branch instead of dev on this PR.

@anupamme
anupamme changed the base branch from dev to main August 4, 2026 06:01
@anupamme

anupamme commented Aug 4, 2026

Copy link
Copy Markdown
Author

Thank you, target main branch instead of dev on this PR.

done.

Comment thread TKLiveSync/unzip.cpp Outdated
Replace heap-allocated PATH_MAX buffer + strcpy/dirname with std::string
find_last_of to avoid silent truncation of long ZIP entry names that could
cause the directory path to diverge from assetFullname.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@TKLiveSync/unzip.cpp`:
- Around line 47-55: Validate each archive entry name in unzip() before
constructing directory paths or opening files: reject absolute paths and any
path component exactly equal to "..". Treat invalid names as failed entries and
skip further processing, ensuring mkdir_rec() and fopen() are never called for
them.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 441a7f98-d430-4747-bfef-2104aa141350

📥 Commits

Reviewing files that changed from the base of the PR and between 77b1ea3 and da985c2.

📒 Files selected for processing (1)
  • TKLiveSync/unzip.cpp

Comment thread TKLiveSync/unzip.cpp
Comment on lines +47 to +55
std::string entryName{ name };
auto separator = entryName.find_last_of('/');

if (separator != std::string::npos) {
std::string dirFullname{ destination };
dirFullname.append("/");
dirFullname.append(entryName.substr(0, separator));
mkdir_rec(dirFullname.c_str());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repository files relevant to unzip:"
git ls-files | rg '(^|/)unzip\.(cpp|h)$|TKLiveSync/TKLiveSync\.m|TKLiveSync/TKLiveSync\.mm$' || true

echo
echo "unzip.cpp outline:"
ast-grep outline TKLiveSync/unzip.cpp || true

echo
echo "unzip.cpp relevant lines:"
cat -n TKLiveSync/unzip.cpp | sed -n '1,120p'

echo
echo "unzip.h:"
cat -n TKLiveSync/unzip.h | sed -n '1,140p'

echo
echo "Path traversal behavior model for current construction:"
python3 - <<'PY'
from pathlib import PurePosixPath

def simulate(destination, name):
    separator = name.rfind("/")
    if separator != -1:
        dirFullname = destination + "/" + name[:separator]
    else:
        dirFullname = destination
    assetFullname = destination + "/" + name
    return dirFullname, assetFullname, PurePosixPath(assetFullname)

destination = "/Users/me/Application Support/LiveSync"
examples = ["../outside.txt", "../../outside.txt", "/tmp/outside.txt", "normal.txt", "subdir/../escape.txt"]
for name in examples:
    df, af, pp = simulate(destination, name)
    print(f"name={name!r}")
    print(f"  constructed_asset_fullname={af!r}')
    print(f"  posix_absolute={pp.is_absolute()}")
    print(f"  canonical_resolved_without_sysfs={pp.resolve() if str(pp).startswith('/') else 'depends on cwd'}")
    print(f"  startsWithDestinationCanonical=False")
PY

Repository: NativeScript/ios

Length of output: 3777


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import PurePosixPath

def simulate(destination, name):
    separator = name.rfind("/")
    if separator != -1:
        dirFullname = destination + "/" + name[:separator]
    else:
        dirFullname = destination
    assetFullname = destination + "/" + name
    return dirFullname, assetFullname, PurePosixPath(assetFullname)

destination = "/Users/me/Application Support/LiveSync"
examples = [
    "../outside.txt",
    "../../outside.txt",
    "/tmp/outside.txt",
    "normal.txt",
    "subdir/../escape.txt",
    "foo/../../escape.txt",
    "foo/bar/../../../escape.txt",
]
for name in examples:
    df, af, pp = simulate(destination, name)
    print(f"name={name!r}")
    print(f"  constructed_asset_fullname={af!r}")
    print(f"  posix_absolute={pp.is_absolute()}")
    print(f"  starts_with_traversal={name.startswith(('../', '/'))}")
PY

echo
echo "Search for unzip calls and LiveSync archive handling:"
rg -n "unzip\\(|sync\.zip|LiveSync" TKLiveSync

Repository: NativeScript/ios

Length of output: 2779


🌐 Web query:

libzip zip_get_name zip files ZIP filenames unsafe absolute paths parent directory traversal

💡 Result:

The use of filenames retrieved from ZIP archives—via functions like libzip's zip_get_name—poses a significant risk of directory traversal attacks [1][2][3]. This is not a vulnerability in libzip itself, but rather a standard security risk inherent in the ZIP file format, which allows entry names to contain absolute paths (e.g., /etc/passwd) or parent directory traversal sequences (e.g.,../../../) [1][4][3]. When an application uses these filenames to construct local file paths on the filesystem, it can lead to arbitrary file creation or overwriting outside the intended destination directory [1][2][5][3]. This class of vulnerability is commonly referred to as a ZipSlip attack [3]. To mitigate this risk, developers must treat any filename obtained from a ZIP archive as untrusted input [2][3]. You must validate and sanitize these paths before using them in filesystem operations [1][3]. Essential security practices include: 1. Canonicalization: Convert the target destination path and the extracted filename into their canonical forms to resolve any traversal sequences [3]. 2. Validation: After canonicalization, verify that the resulting path still resides within the intended destination directory (e.g., by checking if the canonical path starts with the prefix of the safe destination directory) [3]. 3. Disallow Absolute Paths: Explicitly check for and reject filenames that are absolute paths or attempt to escape the designated extraction root [1][3]. libzip provides the raw, unmodified names as stored in the archive [6][7]. It is the responsibility of the application developer to perform the necessary path normalization and security checks before opening or creating files based on these names [7][3].

Citations:


Path Traversal (CWE-22): Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Reachability: External

Treat archive entry names as untrusted before writing files.

sb.name is appended to both the directory path and assetFullname unchanged. Entries with .., /../, or absolute paths such as /tmp/outside.txt make macOS resolve paths back toward filesystem root for later segments, so unzip() can create files or overwrite contents outside the LiveSync destination. Reject absolute names and any .. path component before calling mkdir_rec() or fopen(), and fail the entry on invalid names.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@TKLiveSync/unzip.cpp` around lines 47 - 55, Validate each archive entry name
in unzip() before constructing directory paths or opening files: reject absolute
paths and any path component exactly equal to "..". Treat invalid names as
failed entries and skip further processing, ensuring mkdir_rec() and fopen() are
never called for them.

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.

2 participants