• src/syncterm/ripper.c

    From Deucе@VERT to Git commit to main/sbbs/master on Sun Mar 15 01:06:05 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/bb2238f684befe43deb34cea
    Modified Files:
    src/syncterm/ripper.c
    Log Message:
    Fix heap buffer overflows in ripper.c RIPscrip command handling

    Four strcat() calls append RIPscrip arguments (from the remote server)
    to cache_path[MAX_PATH+1] without checking whether the result fits.
    The path-traversal guards reject "..", "/", and "\" but do not limit
    length. A long filename from a malicious RIPscrip server overflows
    the buffer.

    Changed to strlcat(cache_path, ..., sizeof(cache_path)) at all four
    sites: file-query (&args[6]), icon-load (&args[9] + ".ICN"), and
    icon-save (&args[1]). The existing SkyPix download path already had
    a strlen() guard and was not affected.

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

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Sun Mar 15 14:09:13 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/2054747bb2823818ea5d1a0d
    Modified Files:
    src/syncterm/ripper.c
    Log Message:
    Fix multiple ripper.c security and correctness bugs

    Security fixes:
    - Add path traversal checks (..//\) to LOAD_ICON, WRITE_ICON,
    ENTER_BLOCK_MODE, and font file loading
    - Add overflow guard for ICN pixel buffer allocation (32-bit)
    - Clamp viewport coordinates to world frame dimensions
    - Cap handle_command_str recursion depth to 64
    - Fix sprintf stack overflow in FILE_QUERY case 4 (snprintf)
    - Guard parse_string NULL return in do_rip_command
    - Guard strdup NULL return in bicmp

    Correctness fixes:
    - Remove incorrect viewport offsets from EXTENDED_TEXT_WINDOW (v2+)
    - Fix MOUSE hot field y2 using viewport.sx instead of .sy
    - Fix POLY_LINE y1 init using x_dim instead of y_dim
    - Fix conn_send length for FILE_QUERY \r\n responses (2 -> 3)
    - Fix draw_pixel XOR mode memory leak (freepixels before return)
    - Fix ansi_only() missing break before fall-through
    - Reject zero dimensions in SET_WORLD_FRAME
    - Clamp do_popup dimensions to screen size
    - Fix init_rip_ver memory leaks (mouse fields, clipboard, scb)
    - Add Amiga font file validation at load time
    - Add per-case argc checks in do_skypix
    - Handle realloc failure in reinit_screen gracefully
    - Add NULL checks for getpixels in set_line and flood fill

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

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Sun Mar 15 22:51:39 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/5ca54e09393c1068e32e599f
    Modified Files:
    src/syncterm/ripper.c
    Log Message:
    Fix draw_button() off-by-one errors for exclusive box coordinates

    box.x2/y2 are exclusive (one past end), so:
    - Sunken border right/bottom highlight lines drew one pixel too far out
    - Recessed border width/height were one pixel too large, pushing the
    outer border off-screen for full-width buttons

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

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Tue Mar 17 11:59:17 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/e9b4206eb16d93e29dd10df7
    Modified Files:
    src/syncterm/ripper.c
    Log Message:
    Replace dead argc check with malloc NULL check in do_skypix()

    The argc < 1 guard was unreachable because the counting loop always
    increments argc at least once. Replace it with a NULL check on the
    malloc() result, which was the actual missing guard.
    (Coverity CID 501977)

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Tue Mar 17 11:59:17 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/7350fd498615bb280d1244dd
    Modified Files:
    src/syncterm/ripper.c
    Log Message:
    Check fread() return when loading Amiga font in do_skypix()

    A short read would leave amiga_font partially uninitialized before
    the byte-swapping and offset validation that follows. Matches the
    existing fread check for the font list file earlier in the same
    function. (Coverity CID 501980)

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Sat Mar 21 19:53:02 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/6cce9286f030bf47bd57e6d5
    Modified Files:
    src/syncterm/ripper.c
    Log Message:
    Fix three |# (RIP_NO_MORE) parsing bugs in parse_rip()

    Bug reported by NightFox, who should really open bug reports on
    SourceForge instead of making people chase things down secondhand.

    1. Stale '!' rendered when |# follows non-RIP text

    When non-RIP bytes precede '!' in the same buffer (e.g. the common
    case of \n\r or \r\n line endings before a RIP line), rip_start is
    non-zero. The |# handler called handle_rip_line() which took its
    first branch: deferring the RIP data to pending, truncating blen to
    rip_start, and returning false. But the handler unconditionally ran
    rip_start = pos + 1, which now exceeded blen. At the end of
    parse_rip(), rip_start <= maxlen was true but rip_start > blen, so
    buffer_rip was skipped but rip_start was returned as the output
    length — reading the stale '!' byte from the physically-uncleared
    buffer position. Commit 27e6a20f added the rip_start <= blen guard
    to prevent a crash but did not fix the data leak.

    Fix: check handle_rip_line()'s return value. When it returns false
    (deferred), don't modify rip_start. When it returns true (processed
    immediately), do the post-processing as before. Also pass
    RIP_STATE_BANG instead of RIP_STATE_CMD since both paths want BANG
    state after |#.

    2. |# must flush immediately, even when deferred

    The entire purpose of the |# special handling (added in 93e05beb)
    is to flush RIP commands without waiting for a line terminator.
    When handle_rip_line() defers (returns false), the RIP data sits
    in pending unprocessed until the next parse_rip() call — defeating
    the flush semantics. This matters for interactive commands like
    the NY2008 popup (|1000) that block waiting for user input: the
    popup wouldn't appear until after the next data arrived.

    Fix: when handle_rip_line() returns false, process pending
    immediately via do_rip_string() and reset newstate to FLUSHING
    so the next flush call knows there's nothing left to process.

    3. |# bytes leak as visible text from moredata

    When handle_rip_line()'s first branch defers RIP data, remaining
    bytes after |# are saved to moredata. On the next parse_rip()
    call, the flush path processes pending and switches to moredata.
    But handle_rip_line() unconditionally resets rip_start to the
    sentinel (maxlen+1), even though the restored state (BANG) means
    the moredata buffer is entirely RIP data. With rip_start as
    sentinel, the |# handler's handle_rip_line() call took the second
    path with remove=0, failing to remove the |# bytes from the
    buffer. They were then returned to the caller as visible text.

    Fix: after the flush path switches to moredata, re-check the
    restored state. If it's not BOL/MOL (i.e. still inside a RIP
    sequence), reset rip_start to 0.

    4. Allow '!' to start new RIP sequence after |#

    Per the spec, |# means "end of RIPscrip scene." Testing against
    RIPterm (reference implementation) confirms that '!' after |# on
    the same line starts a new RIP sequence. SyncTERM's BANG state
    only accepted '|', sending '!' to unrip_line -> MOL where it was
    rejected as a RIP start character.

    Fix: accept '!', '\x01', and '\x02' in RIP_STATE_BANG, updating
    rip_start and staying in BANG. This matches RIPterm's behavior
    where both |#|#|# (repeated NO_MORE) and |#!|... (new sequence)
    work on the same line.

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Sat Mar 21 21:02:24 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/c438d8a8d55fcc325c3911af
    Modified Files:
    src/syncterm/ripper.c
    Log Message:
    Restrict post-|# bang acceptance to new RIP_STATE_NO_MORE state

    The '!' acceptance added in 6cce9286f0 was in RIP_STATE_BANG, which
    is also entered from BOL when the initial '!' is seen. This caused
    "!!|c04|..." to treat the second '!' as a new RIP sequence start
    instead of falling out of RIP parsing — the entire line should
    display as literal text since '!!' is not a valid RIP start.

    Add RIP_STATE_NO_MORE, entered only by |# (RIP_NO_MORE). Only this
    state accepts '!'/CTRL-A/CTRL-B for starting a new RIP sequence.
    RIP_STATE_BANG reverts to only accepting '|'.

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net