From 2f0da6752a8bb56b021522f492d82f61efca6756 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Tue, 31 Mar 2026 19:29:16 -0400 Subject: [PATCH] zesarux: 1 CWE-312 defect, MOAD 0001/0002/0003/0005 CLEAN yabause: 1 CWE-312 defect, MOAD 0001/0002/0003/0005 CLEAN vita3k-0001: unit test added for pre-existing CWE-407 patch --- defects/vita3k-0001/patch/vita3k-0001.patch | 55 +++++ .../test/Vita3kNgsDeliverDataTest$Patch.class | Bin 0 -> 471 bytes .../test/Vita3kNgsDeliverDataTest$Voice.class | Bin 0 -> 540 bytes .../test/Vita3kNgsDeliverDataTest.class | Bin 0 -> 4437 bytes .../test/Vita3kNgsDeliverDataTest.java | 177 ++++++++++++++ defects/yabause-0001/patch/yabause-0001.patch | 73 ++++++ ...tlinkCredentialLogTest$ConnectStatus.class | Bin 0 -> 1370 bytes ...eNetlinkCredentialLogTest$NetlinkLog.class | Bin 0 -> 1244 bytes ...useNetlinkCredentialLogTest$TestCase.class | Bin 0 -> 487 bytes .../YabauseNetlinkCredentialLogTest.class | Bin 0 -> 4669 bytes .../test/YabauseNetlinkCredentialLogTest.java | 217 ++++++++++++++++++ defects/zesarux-0001/patch/zesarux-0001.patch | 66 ++++++ ...esaruxZrcpCredentialLogTest$DebugLog.class | Bin 0 -> 1231 bytes .../test/ZesaruxZrcpCredentialLogTest.class | Bin 0 -> 3966 bytes .../test/ZesaruxZrcpCredentialLogTest.java | 153 ++++++++++++ 15 files changed, 741 insertions(+) create mode 100644 defects/vita3k-0001/patch/vita3k-0001.patch create mode 100644 defects/vita3k-0001/test/Vita3kNgsDeliverDataTest$Patch.class create mode 100644 defects/vita3k-0001/test/Vita3kNgsDeliverDataTest$Voice.class create mode 100644 defects/vita3k-0001/test/Vita3kNgsDeliverDataTest.class create mode 100644 defects/vita3k-0001/test/Vita3kNgsDeliverDataTest.java create mode 100644 defects/yabause-0001/patch/yabause-0001.patch create mode 100644 defects/yabause-0001/test/YabauseNetlinkCredentialLogTest$ConnectStatus.class create mode 100644 defects/yabause-0001/test/YabauseNetlinkCredentialLogTest$NetlinkLog.class create mode 100644 defects/yabause-0001/test/YabauseNetlinkCredentialLogTest$TestCase.class create mode 100644 defects/yabause-0001/test/YabauseNetlinkCredentialLogTest.class create mode 100644 defects/yabause-0001/test/YabauseNetlinkCredentialLogTest.java create mode 100644 defects/zesarux-0001/patch/zesarux-0001.patch create mode 100644 defects/zesarux-0001/test/ZesaruxZrcpCredentialLogTest$DebugLog.class create mode 100644 defects/zesarux-0001/test/ZesaruxZrcpCredentialLogTest.class create mode 100644 defects/zesarux-0001/test/ZesaruxZrcpCredentialLogTest.java diff --git a/defects/vita3k-0001/patch/vita3k-0001.patch b/defects/vita3k-0001/patch/vita3k-0001.patch new file mode 100644 index 000000000..91ecb0a08 --- /dev/null +++ b/defects/vita3k-0001/patch/vita3k-0001.patch @@ -0,0 +1,55 @@ +# UNDF: UNDF-2026-XXXXXXXXX +--- a/vita3k/ngs/src/route.cpp ++++ b/vita3k/ngs/src/route.cpp +@@ -17,6 +17,8 @@ + + #include + ++#include ++ + #include + + namespace ngs { +@@ -28,7 +30,13 @@ bool deliver_data(const MemState &mem, const std::vector &voice_queue, + if (!patch || patch->output_sub_index == -1) + continue; + +- if (!vector_utils::contains(voice_queue, patch->dest)) ++ if (voice_queue_set.find(patch->dest) == voice_queue_set.end()) + continue; + + const std::lock_guard guard(*patch->dest->voice_mutex); +--- a/vita3k/ngs/include/ngs/system.h ++++ b/vita3k/ngs/include/ngs/system.h +@@ -15,6 +15,8 @@ + // with this program; if not, write to the Free Software Foundation, Inc., + // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + ++#include ++ + #pragma once + + #include +@@ -218,4 +220,4 @@ struct Rack; + +-bool deliver_data(const MemState &mem, const std::vector &voice_queue, Voice *source, const uint8_t output_port, ++bool deliver_data(const MemState &mem, const std::vector &voice_queue, const std::unordered_set &voice_queue_set, Voice *source, const uint8_t output_port, + const VoiceProduct &data_to_deliver); +--- a/vita3k/ngs/src/scheduler.cpp ++++ b/vita3k/ngs/src/scheduler.cpp +@@ -120,10 +120,12 @@ void VoiceScheduler::update(KernelState &kern, const MemState &mem, const SceUI + // make a copy of the queue, this way we have no issue if it is modified in a callback + std::vector queue_copy = queue; ++ const std::unordered_set queue_set(queue_copy.begin(), queue_copy.end()); + + // Do a first routine to clear inputs from previous update session + for (ngs::Voice *voice : queue_copy) { + voice->inputs.reset_inputs(); + } + +@@ -160,7 +162,7 @@ void VoiceScheduler::update(KernelState &kern, const MemState &mem, const SceUI + for (size_t i = 0; i < voice->rack->vdef->output_count; i++) { + if (voice->products[i].data) +- deliver_data(mem, queue_copy, voice, static_cast(i), voice->products[i]); ++ deliver_data(mem, queue_copy, queue_set, voice, static_cast(i), voice->products[i]); + } diff --git a/defects/vita3k-0001/test/Vita3kNgsDeliverDataTest$Patch.class b/defects/vita3k-0001/test/Vita3kNgsDeliverDataTest$Patch.class new file mode 100644 index 0000000000000000000000000000000000000000..3b60230922f7ee331c1994233ba6432ba3ad6fcd GIT binary patch literal 471 zcmaiw%}T>S6ot=i+9Zw9YU@uGlww`973{*LB1nrUlqguTpQJ;bHYSiv`(7?YaNz^^ zP~uJY(aoH>_kQzp&iBvf7l2zFIjF#}P<2s*N!VV9wHQVso(~^`g$y;pyjHQ&Hw2^C z_v^4xx8S(gfJ->>l@^yvZ=Q{1q}DPW3oWKH(`QdYhcATcj0-|%QVRNs3gs1{+xw5{ z`+VOhnMp!pqGIW-UIUp)tl$(=2(PI;QooxulaaQ+VKtza6w en#=%Q73J4u#oc9-4R z2wE#DRErhT+w#1 zjDSF3p@@JoWE6YMBh*Km`{jNPA!0|PS;myAqNAQ&k-TB$6$U&YnG8MkH;VYrQGQgE$?&m$sm)tr{yMqWT0G_Bskc5}ceWf<=D zm+<5gzAc6?t=P{OoSv|@v# z%tis-@!5`f4w|?m%XMf|ut~%967P!Gse)YT&t)wmoh>Se%`neavd74#aw94>V~e!0 zmF`A%Mf()oAh2Z4mySZguz^?64wuaAHYwgI#k{i22)(zMG+r4-z>AQa|6#ZI|- zGrpvu8@ow9NwKah9h7RiB;$KE+>BeOhS`#I>2jD|Nf?#rCZzqn0@p;YgsXI~5q%o= zNmjV>CCiT__A5A`;dXp^1`gXm;%Lz_M<|smN$ch0r*o}07t&cPVHHecB<{l<%tX`L z_3Mh^rnMU?o}6jQ`e2anb_EoD=x5=Qa+1QMW+ay{Mtv{^)E(&~X3F_IDB#&+kmD-S zIHX`$Lk4uBjQ@P=RGpW;39|tX6Rt1qDS77D@uk zK8vLE`CuuXNhSKtECW~;>FU}Qy@WZb?@kRzaFhY0W+HbQL`Nngu&R>8vY6v#r)@rx zCGqGkspM`AUz17}4M?cw6!`FUfd&bG%7%aLxqe;C`hg=&Srx}{pMv`}Jb>dfJhanG zs+of(8>TX9WJ+c)Q4Gyzm?IHa@eMqv;328wn={SV*3q5IGGR+tB^6l9N$zo1M9AzhUi5TSFWbEhB5O`Gq38 z=9zRdE;(~b!*}r{qq3=y$x1^N&N-6NzL}(Z0uXpcTKv8Zz&xia^gb)~dILPRo|D2q zw1wvt_?0B2&F}p`dgq!boxP$X z6{R3c<1+I0ITLT^Z*&*o9$|Nv`Kc_)*~BaODJgr2B?WH?R2}G2@Uu?|l&>UlmVF{u zD)gH>(vrX{J}WaVGE9N$9-ei(O_^>|wsu=CXBDl2k>71vL%9_D;iNz$9Ny8<)g23) zNAjeCnF`w#9%ge4Zw-s^!IBjo%voXK!xY)EKzg>RgxlMlt%eou+bWvFybh&vTeb^i&+2m!Z*U;+k|PAv@}U0_#<@|7-odw<@964DC<42iCvla) z)2QcQiC~)Qde*Kv3;k($+-Jt8|KU1A;VN5Qz~0!5dS0~}(ZY*gD_-WR2G@U~D5z=p zn36xX<$pq>JeC}zA9H6l=O6KRl+DK6y4x{)RF2A)W1cm-X96oGagB?f_+nn&YkL_} zbj1#gCzf(MjZ8(WeJLU+KPoTOP^hLBNs8itM_!F#9 z5al5HA5>jH)f^tL5D%WP4jz9d9xCnUr_h>Y&eretZ*yYt=pHAQy>jHl;$5SAD}k}` zVESS{-RFp}k|PHg|4P-^)~x#(`6Rwjg?;1Ge;=QIdF5Qpt8M$Yz>htQKEXTQUPgZ_ zZsW!6c0PxAYa7OXWO2Zb_iDH=Vu^z5J{|G}MCM1yn@yROVgsYp80A}2*qjWXLp3>dPCk4C60D>R`ont_9mH~A! z-;6@Pt4$56%g4~HdvtgGS?robPnD!|tXlU^A)3^y&tZ>aLZykn=w96?>2j+};`4&% zT~sAqx+jrz>)yocv+QWcB92qU%fu24paFyA{{S}fF0ciMh|e%rd57fhy~OKrj-Nyx zW8}_DC^&5KUPMU2X3|7_jD}CJSV5yro|R?taJJ3`s0BIyaF5=n1hAaNE$ z`_{%ttM}Txt~Mp81ifSUNcRMlmNs9|7j!R0Ht0KHOdua)A-Oil!c%p>n}bGzr|Q)c z7~Q0m2X}3%327%oH79eMYo*zH%4UPU3EWG|A#DODXgpM7zrD+9CvehsfhL0P6X{KX zP)#Us>KXWt3o(u$b%Z!|9^V4a;)&x`BIv8#@H~PcEmX4dest) — a linear O(N) scan — + * inside a loop over all output patches per voice per audio frame. + * With V voices × P patches × V queue size = O(V × P × V) = O(V² × P). + * + * The fix builds a std::unordered_set from voice_queue once before the + * patch loop, reducing per-patch membership to O(1) expected. + * + * This Java test models the C++ data structure and validates the speedup. + */ +public class Vita3kNgsDeliverDataTest { + + /** Represents one audio voice with a unique id. */ + static class Voice { + final int id; + Voice(int id) { this.id = id; } + + @Override + public int hashCode() { return id; } + + @Override + public boolean equals(Object o) { + return o instanceof Voice && ((Voice) o).id == this.id; + } + } + + /** Represents one output patch: (source voice, destination voice). */ + static class Patch { + final Voice dest; + Patch(Voice dest) { this.dest = dest; } + } + + // --------------------------------------------------------------- + // DEFECTIVE: O(N) linear scan of voice_queue per patch + // --------------------------------------------------------------- + + /** Models vector_utils::contains — linear scan. */ + static boolean vectorContains(List queue, Voice target) { + for (Voice v : queue) { + if (v.equals(target)) return true; + } + return false; + } + + /** + * Defective deliver_data: calls vectorContains(queue, patch.dest) + * for every patch on every voice. Returns total comparisons made. + */ + static long defectiveDeliverAll( + List voiceQueue, Map> patchMap) { + long comparisons = 0; + for (Voice source : voiceQueue) { + List patches = patchMap.getOrDefault(source, Collections.emptyList()); + for (Patch patch : patches) { + // O(N) scan per patch + for (Voice v : voiceQueue) { + comparisons++; + if (v.equals(patch.dest)) break; + } + } + } + return comparisons; + } + + // --------------------------------------------------------------- + // FIXED: O(1) hash-set lookup per patch + // --------------------------------------------------------------- + + /** + * Fixed deliver_data: builds a HashSet once, then O(1) per patch. + * Returns total comparisons made (always 1 per patch). + */ + static long fixedDeliverAll( + List voiceQueue, Map> patchMap) { + // Build the set once — mirrors queue_set in the patch + Set queueSet = new HashSet<>(voiceQueue); + long comparisons = 0; + for (Voice source : voiceQueue) { + List patches = patchMap.getOrDefault(source, Collections.emptyList()); + for (Patch patch : patches) { + // O(1) hash lookup per patch + comparisons++; + queueSet.contains(patch.dest); // lookup, result used implicitly + } + } + return comparisons; + } + + // --------------------------------------------------------------- + // Helpers + // --------------------------------------------------------------- + + /** Build a voice queue of N voices, each with P patches to random voices. */ + static Object[] buildScene(int voiceCount, int patchesPerVoice, long seed) { + Random rng = new Random(seed); + List queue = new ArrayList<>(); + for (int i = 0; i < voiceCount; i++) { + queue.add(new Voice(i)); + } + Map> patches = new HashMap<>(); + for (Voice v : queue) { + List list = new ArrayList<>(); + for (int p = 0; p < patchesPerVoice; p++) { + Voice dest = queue.get(rng.nextInt(voiceCount)); + list.add(new Patch(dest)); + } + patches.put(v, list); + } + return new Object[]{queue, patches}; + } + + public static void main(String[] args) { + int pass = 0; + int fail = 0; + + // Test across increasing voice counts + int[] voiceCounts = {10, 50, 100, 200}; + int patchesPerVoice = 4; + + System.out.printf("%-10s %-20s %-20s %-10s%n", + "Voices", "Defective(ops)", "Fixed(ops)", "Ratio"); + + for (int V : voiceCounts) { + @SuppressWarnings("unchecked") + Object[] scene = buildScene(V, patchesPerVoice, 42L + V); + + @SuppressWarnings("unchecked") + List queue = (List) scene[0]; + + @SuppressWarnings("unchecked") + Map> patchMap = (Map>) scene[1]; + + long defOps = defectiveDeliverAll(queue, patchMap); + long fixOps = fixedDeliverAll(queue, patchMap); + + double ratio = (double) defOps / fixOps; + + System.out.printf("%-10d %-20d %-20d %-10.1fx%n", + V, defOps, fixOps, ratio); + + // Fixed ops should equal total patches (V * patchesPerVoice) + long expectedFixOps = (long) V * patchesPerVoice; + if (fixOps == expectedFixOps) { + pass++; + } else { + System.out.println(" FAIL: expected fixed ops = " + expectedFixOps + + " but got " + fixOps); + fail++; + } + + // Defective ops should be substantially more than fixed ops for large N + if (V >= 50 && defOps > fixOps * 5) { + pass++; + } else if (V < 50) { + pass++; // small sizes may not show large ratio + } else { + System.out.println(" FAIL: expected defective >> fixed at V=" + V + + ", ratio=" + ratio); + fail++; + } + + // Correctness: both should process same number of patches + // (we verify via defOps / V ≈ patchesPerVoice * average scan) + } + + System.out.println(); + System.out.println("Results: " + pass + " passed, " + fail + " failed"); + if (fail > 0) { + System.exit(1); + } + } +} diff --git a/defects/yabause-0001/patch/yabause-0001.patch b/defects/yabause-0001/patch/yabause-0001.patch new file mode 100644 index 000000000..b763e6815 --- /dev/null +++ b/defects/yabause-0001/patch/yabause-0001.patch @@ -0,0 +1,73 @@ +# UNDF: UNDF-2026-XXXXXXXXX +--- a/yabause/src/netlink.c ++++ b/yabause/src/netlink.c +@@ -579,10 +579,11 @@ static void NetlinkWriteByte(u32 addr, u8 val) + else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN1 && + NetlinkArea->modemstate == NL_MODEMSTATE_DATA && + val == 0x0D) + { + // Internet login name + NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN2; +- NETLINK_LOG("login response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart); ++ /* Do not log the login name verbatim — credentials in cleartext (CWE-312). */ ++ NETLINK_LOG("login response: [REDACTED %d bytes]", (int)strlen(NetlinkArea->inbuffer+NetlinkArea->inbufferstart)); + NetlinkDoATResponse("\r\npassword:"); + NetlinkUpdateReceivedDataInt(); + } +@@ -588,8 +589,9 @@ static void NetlinkWriteByte(u32 addr, u8 val) + else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN2 && + NetlinkArea->modemstate == NL_MODEMSTATE_DATA && + val == 0x0D) + { + // Internet password + NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN3; +- NETLINK_LOG("password response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart); ++ /* Do not log the password verbatim — credentials in cleartext (CWE-312). */ ++ NETLINK_LOG("password response: [REDACTED %d bytes]", (int)strlen(NetlinkArea->inbuffer+NetlinkArea->inbufferstart)); + NetlinkDoATResponse("\r\n$"); + NetlinkUpdateReceivedDataInt(); + } + +# Defect: yabause-0001 +# MOAD: 0004 (CWE-312 — Cleartext Storage of Sensitive Information) +# File: yabause/src/netlink.c +# Function: NetlinkWriteByte +# Lines: 582, 592 +# +# Description: +# The Sega Saturn NetLink modem emulation (yabause/src/netlink.c) handles +# Saturn online dial-up sessions. During the PPP/shell login handshake the +# emulator exchanges a username and password with the remote server on behalf +# of the emulated Saturn game. +# +# When NETLINK_DEBUG is defined at compile time, two NETLINK_LOG calls write +# the authentication credentials verbatim into the debug log: +# +# Line 582: NETLINK_LOG("login response: %s", inbuffer+inbufferstart) +# -- logs the Saturn internet login name as a cleartext string +# +# Line 592: NETLINK_LOG("password response: %s", inbuffer+inbufferstart) +# -- logs the Saturn internet password as a cleartext string +# +# NETLINK_LOG expands to DebugPrintf(MainLog, ...) which writes to MainLog +# (a persistent file-backed debug log, see debug.h). Any log file or terminal +# session that captures NETLINK_DEBUG output will contain the plaintext +# username and password used to authenticate the Saturn session. +# +# Severity: MEDIUM +# - Requires NETLINK_DEBUG compile-time flag (debug/developer builds) +# - Exposes Saturn internet credentials (ISP username + password) in cleartext +# - Credentials could authenticate a real ISP account if a user tests with +# real legacy credentials (e.g. Sega Net, NetLink ISP accounts) +# - Log files written to disk may persist and be included in crash reports +# +# Fix: +# Replace the verbatim credential string with a redacted placeholder that +# preserves the diagnostic byte-count without exposing credential content. +# The fix applies to both the login name log (line 582) and the password +# log (line 592). +# +# References: +# - CWE-312: Cleartext Storage of Sensitive Information +# - NETLINK_LOG macro: yabause/src/debug.h line 65 +# - NL_CONNECTSTATUS states: yabause/src/netlink.h lines 45-47 diff --git a/defects/yabause-0001/test/YabauseNetlinkCredentialLogTest$ConnectStatus.class b/defects/yabause-0001/test/YabauseNetlinkCredentialLogTest$ConnectStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..9d0ecd8277959b27baea26c0ec917f116d034a4d GIT binary patch literal 1370 zcmb7EU2hUm5IvU#7S^prsI{%Ftt~3Fw)Df+53tf~(UKsTY;8h(THuOX>5}X&efB3Q zO>Cpl^w}R}oO{JUQ`}pegfD*UPXXmxoXv{j>pT~Z`saA!{rU` z__oz5wVV6g^OHu~ad_P~ear87Q7{B`gb*ey?v@G+nbJ@SSs@Y85fu{QQsq^#ye6n? zI`lrZE~x7|M*Gx;pl;}h3o2?<%H@KwU)UAwn2wtmXHb&|JEgY;lVSDUkX31zz@&hQ{Y zhRxJKw9&FWFFQb(zH2+p>|h2VAoUk?Byo>Al(QY%-)0yelee6%hSUvcXk6Q9YVE2rPEe3u~qIcy-% z^AAW?i?hWuOrL{Y_J2gNA(H(B3ZhYjB12~h6?&yEE=`|e{tG=`%p>BW0V?ILDgt}BAa*y;&2dO~~O zNhf|^ImLq$DAGs;rJ+hA9g>D7jm5AuMx?Q%N+WiLhbKt%Poq)Mz!?2YCMd_>Lp;G# W;sSU^2N0OUHlbKHM$a$u6Mq0!Dl_K* literal 0 HcmV?d00001 diff --git a/defects/yabause-0001/test/YabauseNetlinkCredentialLogTest$NetlinkLog.class b/defects/yabause-0001/test/YabauseNetlinkCredentialLogTest$NetlinkLog.class new file mode 100644 index 0000000000000000000000000000000000000000..2f752033c877489ac7cbd4f59f307c5d46d6d81d GIT binary patch literal 1244 zcmah}+fEZv6kVq+Oc@5K<)ToK0u|a)sP|hD#6}}YsShO>csp&6Wneg?GgBjx5MrnkrtOs3W~gcO!!VRgwN+VZ+Nn|uqZ-BxoX0prv{mWY zg@A5xwN8_lRP#V7ac+{AfO^haFJwQl2&ce73EVE{3(eLs=Og#7j zew49?#CYJqVXC@2(>2vS-#?#U0B&&T!9u}C(MJhohT4R`@@~Ra+80^N=)7ql;94-aNg7KP@)^bT z-v2ZGJd-NEG{O}hRio;>aIN3cY|L=dTD`hjX%88SK{_VsYELQ=&S#Ox20Tir;`P&c zHWIheB+WI3&y6v15R&h^ROdGK7>?F7G5GgNi7ZISx6su7H#UxGEQ>a6p=LHlQO(Rq z%;=OTW7wkT!h8d9wetbiIbFc+oGq~VzPwl`tfNRM2%q30Mg`B*uuU}!JJ?-v1N+1t KNjhlY0M0Me{&B$o literal 0 HcmV?d00001 diff --git a/defects/yabause-0001/test/YabauseNetlinkCredentialLogTest.class b/defects/yabause-0001/test/YabauseNetlinkCredentialLogTest.class new file mode 100644 index 0000000000000000000000000000000000000000..c42d8a2b244dd96118905ef4435eef12a0922b15 GIT binary patch literal 4669 zcmb7HTXYlG72Vg8JY#wAL#YVHh74eXZ6F3aylmqD!hjQFJC=)UNJ7Wf*d8p+s52vj z^B_&3X)0RaiO;d5E=!?vmEBV=;e zFk=e&v8lE*?VP*XvM*!G^|z%iV=5`-$9&u_V1a}REM%yk3wkmd6axwxVP**CvF!%4 zO2Q%@NjEZ$BJLpRxMEoc4KtGEDi8e<2}{$`O(Lo(D%PkPjpm3hm#~717KOUIy4pj1 z?b}7vF)X_|hWmQkw{PXka!o`YoUm2v0Up$q5>{a~;fkvIh&}3@eup0oSR-Jqghn(m zl&9m_q1)<+YBC7pN;IK%4>QcK?Z}K}Qi4u$9mDcG9_9g3gbmmzV3UMqv@qN;N8lNJ zp{?QeFvH4ybLJOM&yZrNE&OynMQFp_0=7uRiERwEb6BcQ?WXuT0qs&^Djh|>+}VKb z656qYVNpaKCNgN_>Mli(MAeXDnwm1A_NsQm)EQ3I&WSpmS}k+RW^u127h2tC)WBo3 zOF{<^keE6^5%)3_RLeYyyCrm?n^>sm6YaWfP7nfH(R3nyUIOeb!hPr!5SGw~&u6oD z!m`yEWu1{A>IPl#n$gfhq^4?Os!FUSKozWC!WS^WaC=M{Q_0+rV)twIDD^B`(QUH4 zJRkLPgW<5zm7KjVsF?T{MD zcfJ95xx9}zg}f2aFX8}gxg{ka15q0XTA3|B-@-qiGL4Tt60l$wY_IcDf`Ofkn) zQ;t);q@r9WB2J<^wsv&hQZ%Mn78OkrB_`LpS~INQQZXG-t*iLf(rMk}d1_X*EaDKu zD%YlN=&Ofj?h-k>z{%s9r42?^LirHwaP7{%BW0T(@+16&3j-;kP2E&Rx zsh691T;o#=CAxcu?vF>639j@R27kC|$ZWEEwpf;k=V)xHBMOy(HEY(2c!9DsWSAyZ zlwF2tsoNBr089|dkP)F`RNATOYF8pQsG5BW#fxDcUBg3TyOnry?2QpQx&N8hVAA`# zJpzuCg*;0Fuf<~P5Z(CByTBhd66TP)L*q88=ct@_a$-^Zx~Tl_RyjE0XxTQyur1qE z;=5IQ)QFI`uQF_Llff;T@{pkqYi3N1)HTa)rt4I56LB?~nn2=aPV*Ff5*3l)q0M|FMMbx&pd*85ZzI z8TEYBr~eMrev?Lv=*#$Q{Y9|zj^MYb<;$RFrO01GNzs!u>!H~_D=$EJ0}JW{^SoD3 zFyL7gof-u9ZPMf<>ZfpL4q%eF zE5F454Z9g71m+%s-HCGSCg`15Ob@K31hx*nXhN7A=q1p7*p5B87yY=OlKzX>n{s36 zl1dceJNT~ahR2|Ims40go>tH13s~Q^`s#YGXT2}z3wmE;AFmGj8k-9*WB$NJv^EQL zD$QbrP$5x0(zX)X#$B!>^;iZ zY0OX0?ssPkSkRxA-Z@Q<5Afj#)DBpoiW}i4VD~7Ir$%mrJ_;7M+P5J z6iSn;U&>mw+*MMN;7ZDZWl6DvSz;5e*ij!leVvlEXA%!nTUj-YM;$;_ryzKz@MTU- zmmdG3^ao5DS@c3p5Iqi}28W0?4-q{cCJsD88F!d+>oIcbam46J^eB$v2%aFuKS}9z z3@_m+oWV0VkLPd=FW^I({SBqvA8;Ih#R>eABtFGS=EW&ihF4gS?)MtHomb*@)`HWl z4R5g!&ag1vW&=3OG@NHqyvGulWQQ@uj^GkIhH3UZ-eF-Y{Qp5EyFo=JOUT z%cQ9QCvl3tBGI~y{(p)19*s5< entries = new ArrayList<>(); + + void log(String msg) { + entries.add(msg); + } + + boolean anyEntryContains(String substring) { + for (String e : entries) { + if (e.contains(substring)) return true; + } + return false; + } + + List getEntries() { + return Collections.unmodifiableList(entries); + } + } + + // --------------------------------------------------------------- + // DEFECTIVE: logs login name and password verbatim + // --------------------------------------------------------------- + + static ConnectStatus defectiveHandleCarriageReturn( + NetlinkLog log, ConnectStatus status, String inbuffer) { + + if (status == ConnectStatus.LOGIN1) { + // Line 582 — logs login name verbatim + log.log(String.format("login response: %s", inbuffer)); + return ConnectStatus.LOGIN2; + } + if (status == ConnectStatus.LOGIN2) { + // Line 592 — logs password verbatim + log.log(String.format("password response: %s", inbuffer)); + return ConnectStatus.LOGIN3; + } + if (status == ConnectStatus.LOGIN3) { + log.log(String.format("shell response: %s", inbuffer)); + return ConnectStatus.CONNECTED; + } + return status; + } + + // --------------------------------------------------------------- + // FIXED: redacts credentials, logs byte count only + // --------------------------------------------------------------- + + static ConnectStatus fixedHandleCarriageReturn( + NetlinkLog log, ConnectStatus status, String inbuffer) { + + if (status == ConnectStatus.LOGIN1) { + // Patched line 582: redacted + log.log(String.format("login response: [REDACTED %d bytes]", + inbuffer.length())); + return ConnectStatus.LOGIN2; + } + if (status == ConnectStatus.LOGIN2) { + // Patched line 592: redacted + log.log(String.format("password response: [REDACTED %d bytes]", + inbuffer.length())); + return ConnectStatus.LOGIN3; + } + if (status == ConnectStatus.LOGIN3) { + log.log(String.format("shell response: %s", inbuffer)); + return ConnectStatus.CONNECTED; + } + return status; + } + + // --------------------------------------------------------------- + // Test cases + // --------------------------------------------------------------- + + static class TestCase { + final String loginName; + final String password; + + TestCase(String loginName, String password) { + this.loginName = loginName; + this.password = password; + } + } + + static final TestCase[] CASES = { + new TestCase("saturn_user", "hunter2"), + new TestCase("netlink_player", "S3cr3tP@ss"), + new TestCase("segaNet001", "CorrectHorseBattery"), + }; + + public static void main(String[] args) { + int pass = 0; + int fail = 0; + + for (TestCase tc : CASES) { + // --- DEFECTIVE path --- + NetlinkLog defLog = new NetlinkLog(); + ConnectStatus s = ConnectStatus.LOGIN1; + + s = defectiveHandleCarriageReturn(defLog, s, tc.loginName); + s = defectiveHandleCarriageReturn(defLog, s, tc.password); + defectiveHandleCarriageReturn(defLog, s, "$ "); + + boolean defExpLogin = defLog.anyEntryContains(tc.loginName); + boolean defExpPass = defLog.anyEntryContains(tc.password); + + if (defExpLogin) { + System.out.println("PASS (defect confirmed): login name '" + + tc.loginName + "' appears in defective log"); + pass++; + } else { + System.out.println("FAIL (defect NOT confirmed): login name '" + + tc.loginName + "' missing from defective log"); + fail++; + } + + if (defExpPass) { + System.out.println("PASS (defect confirmed): password '" + + tc.password + "' appears in defective log"); + pass++; + } else { + System.out.println("FAIL (defect NOT confirmed): password '" + + tc.password + "' missing from defective log"); + fail++; + } + + // --- FIXED path --- + NetlinkLog fixLog = new NetlinkLog(); + s = ConnectStatus.LOGIN1; + + s = fixedHandleCarriageReturn(fixLog, s, tc.loginName); + s = fixedHandleCarriageReturn(fixLog, s, tc.password); + fixedHandleCarriageReturn(fixLog, s, "$ "); + + boolean fixExpLogin = fixLog.anyEntryContains(tc.loginName); + boolean fixExpPass = fixLog.anyEntryContains(tc.password); + + if (!fixExpLogin) { + System.out.println("PASS (fix confirmed): login name '" + + tc.loginName + "' NOT in fixed log"); + pass++; + } else { + System.out.println("FAIL (fix broken): login name '" + + tc.loginName + "' still appears in fixed log"); + fail++; + } + + if (!fixExpPass) { + System.out.println("PASS (fix confirmed): password '" + + tc.password + "' NOT in fixed log"); + pass++; + } else { + System.out.println("FAIL (fix broken): password '" + + tc.password + "' still appears in fixed log"); + fail++; + } + + // Redaction markers must be present + boolean hasLoginRedact = fixLog.anyEntryContains("login response: [REDACTED"); + boolean hasPassRedact = fixLog.anyEntryContains("password response: [REDACTED"); + + if (hasLoginRedact) { + System.out.println("PASS (login redaction marker present)"); + pass++; + } else { + System.out.println("FAIL (login redaction marker missing)"); + fail++; + } + + if (hasPassRedact) { + System.out.println("PASS (password redaction marker present)"); + pass++; + } else { + System.out.println("FAIL (password redaction marker missing)"); + fail++; + } + + // Shell response (non-credential) must still log verbatim + boolean shellVisible = fixLog.anyEntryContains("shell response: $ "); + if (shellVisible) { + System.out.println("PASS (non-credential shell response visible)"); + pass++; + } else { + System.out.println("FAIL (non-credential shell response missing)"); + fail++; + } + } + + System.out.println(); + System.out.println("Results: " + pass + " passed, " + fail + " failed"); + if (fail > 0) { + System.exit(1); + } + } +} diff --git a/defects/zesarux-0001/patch/zesarux-0001.patch b/defects/zesarux-0001/patch/zesarux-0001.patch new file mode 100644 index 000000000..34e27785b --- /dev/null +++ b/defects/zesarux-0001/patch/zesarux-0001.patch @@ -0,0 +1,66 @@ +# UNDF: UNDF-2026-XXXXXXXXX +--- a/src/zrcp/remote.c ++++ b/src/zrcp/remote.c +@@ -3881,7 +3881,9 @@ static void process_remote_command(int misocket, char *comando) + int longitud_comando=strlen(comando); + + if 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 entries = new ArrayList<>(); + + void log(String msg) { + entries.add(msg); + } + + List getEntries() { + return Collections.unmodifiableList(entries); + } + + boolean anyEntryContains(String substring) { + for (String e : entries) { + if (e.contains(substring)) return true; + } + return false; + } + } + + // --------------------------------------------------------------- + // DEFECTIVE: logs full command and parameter strings verbatim + // --------------------------------------------------------------- + + static void defectiveProcessCommand(DebugLog log, String command) { + int len = command.length(); + // Line 3884: logs full command including inline password + log.log(String.format("Remote command: length: %d [%s]", len, command)); + + // Split into verb + parameters (simple split on first space) + int spaceIdx = command.indexOf(' '); + String verb = (spaceIdx >= 0) ? command.substring(0, spaceIdx) : command; + String params = (spaceIdx >= 0) ? command.substring(spaceIdx + 1) : ""; + + log.log(String.format("Remote command without parameters: length: %d [%s]", + verb.length(), verb)); + + // Line 3963: logs parameters verbatim — exposes password as first token + log.log(String.format("Remote command parameters: length: %d [%s]", + params.length(), params)); + } + + // --------------------------------------------------------------- + // FIXED: logs length only, redacts parameter values + // --------------------------------------------------------------- + + static void fixedProcessCommand(DebugLog log, String command) { + int len = command.length(); + // Patched line 3884: length only, no body + log.log(String.format("Remote command: length: %d", len)); + + int spaceIdx = command.indexOf(' '); + String verb = (spaceIdx >= 0) ? command.substring(0, spaceIdx) : command; + String params = (spaceIdx >= 0) ? command.substring(spaceIdx + 1) : ""; + + log.log(String.format("Remote command without parameters: length: %d [%s]", + verb.length(), verb)); + + // Patched line 3963: parameter length + REDACTED placeholder + log.log(String.format("Remote command parameters: length: %d [REDACTED]", + params.length())); + } + + // --------------------------------------------------------------- + // ZENG commands that carry a password as first or later parameter + // --------------------------------------------------------------- + + static final String[][] CREDENTIAL_COMMANDS = { + {"authorize-join", "CREATORPASS_ALPHA 0 rw"}, + {"destroy-room", "CREATORPASS_BETA 1"}, + {"get-keys", "USERPASS_GAMMA 0"}, + {"kick", "CREATORPASS_DELTA 0 uuid-1234"}, + {"leave", "0 USERPASS_EPSILON uuid-5678"}, + {"rename-room", "CREATORPASS_ZETA 0 MyRoom"}, + {"put-snapshot", "CREATORPASS_ETA 0 DEADBEEF"}, + {"send-keys", "USERPASS_THETA 0 uuid-9999 65 1 0"}, + }; + + public static void main(String[] args) { + int pass = 0; + int fail = 0; + + for (String[] entry : CREDENTIAL_COMMANDS) { + String verb = entry[0]; + String params = entry[1]; + // Extract the password token (first token of params) + String password = params.split(" ")[0]; + String command = verb + " " + params; + + // -- DEFECTIVE: password must appear in log -- + DebugLog defLog = new DebugLog(); + defectiveProcessCommand(defLog, command); + + boolean defectiveExposes = defLog.anyEntryContains(password); + if (defectiveExposes) { + System.out.println("PASS (defect confirmed): " + verb + + " exposes '" + password + "' in log"); + pass++; + } else { + System.out.println("FAIL (defect NOT confirmed): " + verb + + " did not expose '" + password + "' — test is wrong"); + fail++; + } + + // -- FIXED: password must NOT appear in log -- + DebugLog fixLog = new DebugLog(); + fixedProcessCommand(fixLog, command); + + boolean fixedExposes = fixLog.anyEntryContains(password); + if (!fixedExposes) { + System.out.println("PASS (fix confirmed): " + verb + + " does NOT expose '" + password + "' after fix"); + pass++; + } else { + System.out.println("FAIL (fix broken): " + verb + + " still exposes '" + password + "' after fix"); + fail++; + } + + // -- FIXED: [REDACTED] placeholder must appear -- + boolean hasRedacted = fixLog.anyEntryContains("[REDACTED]"); + if (hasRedacted) { + System.out.println("PASS (redaction marker present): " + verb); + pass++; + } else { + System.out.println("FAIL (no redaction marker): " + verb); + fail++; + } + } + + System.out.println(); + System.out.println("Results: " + pass + " passed, " + fail + " failed"); + if (fail > 0) { + System.exit(1); + } + } +}