fix(adhoc-sweep-fixes): CU-86akdypw4 16 review findings across 16 files - #166
fix(adhoc-sweep-fixes): CU-86akdypw4 16 review findings across 16 files#166flamingo[bot] wants to merge 16 commits into
Conversation
| obj.onSecureConnect = function onSecureConnect(resp, ws, head) { | ||
| Debug("APF Secure WebSocket connected."); | ||
| //console.log(JSON.stringify(resp)); | ||
| obj.forwardClient.tag = { accumulator: [] }; |
There was a problem hiding this comment.
🦩 🟠 amt-apfclient.js accumulator uses += on array/string mismatch (tag.accumulator initialized as array, appended as string)
In obj.onSecureConnect, changed obj.forwardClient.tag = { accumulator: [] }; to obj.forwardClient.tag = { accumulator: '' };. This makes the initializer's declared type match the actual runtime type produced by the += string concatenation in the subsequent data event handler and consumed by .charCodeAt()/.slice()/.substring() calls throughout ProcessData, eliminating the silent array-to-string coercion.
🤖 Prompt for AI agents
In agents/modules_meshcmd/amt-apfclient.js around line 147, review and complete this code-review fix: amt-apfclient.js accumulator uses += on array/string mismatch (tag.accumulator initialized as array, appended as string).
What the draft fix changed: In `obj.onSecureConnect`, changed `obj.forwardClient.tag = { accumulator: [] };` to `obj.forwardClient.tag = { accumulator: '' };`. This makes the initializer's declared type match the actual runtime type produced by the `+=` string concatenation in the subsequent `data` event handler and consumed by `.charCodeAt()`/`.slice()`/`.substring()` calls throughout `ProcessData`, eliminating the silent array-to-string coercion.
Verify the change is correct and complete; do not refactor unrelated code.
fix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer
| } | ||
| } catch (xx) { } | ||
| } else { | ||
| throw('Unknown board'); |
There was a problem hiding this comment.
🦩 🟠 linux_identifiers() throws bare strings instead of Error objects
In linux_identifiers(), replaced throw('Unknown board'); with throw (new Error('Unknown board')); and throw ('this platform does not have DMI statistics'); with throw (new Error('this platform does not have DMI statistics'));. Both throws now produce proper Error objects so callers relying on e.message/e.stack get correct values. No other throw sites (e.g. the unrelated default-platform throw ('Unsupported Platform') outside this function) were touched since the finding scoped this to linux_identifiers().
🤖 Prompt for AI agents
In agents/modules_meshcore/computer-identifiers.js around line 92, review and complete this code-review fix: linux_identifiers() throws bare strings instead of Error objects.
What the draft fix changed: In `linux_identifiers()`, replaced `throw('Unknown board');` with `throw (new Error('Unknown board'));` and `throw ('this platform does not have DMI statistics');` with `throw (new Error('this platform does not have DMI statistics'));`. Both throws now produce proper Error objects so callers relying on `e.message`/`e.stack` get correct values. No other throw sites (e.g. the unrelated default-platform `throw ('Unsupported Platform')` outside this function) were touched since the finding scoped this to `linux_identifiers()`.
Verify the change is correct and complete; do not refactor unrelated code.
fix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer
| sms = require('url').parse(sms); | ||
| if (sms.protocol == 'https:') { | ||
| // HTTPS GET request | ||
| const options = { hostname: sms.hostname, port: sms.port ? sms.port : 443, path: sms.path, method: 'GET', rejectUnauthorized: false }; |
There was a problem hiding this comment.
🦩 🟠 SMS 'url' provider issues plaintext HTTP GET with attacker-influenced query string and rejectUnauthorized disabled
In obj.sendSMS (the url provider branch, HTTPS request options), changed the unconditional rejectUnauthorized: false to rejectUnauthorized: (parent.config.sms.allowunauthorizedcert === true) ? false : true. TLS certificate validation is now enabled by default and only disabled if an operator explicitly sets a new config flag sms.allowunauthorizedcert: true in config.json. This requires an explicit opt-in rather than unconditionally weakening TLS. Unverified/left to reviewer: whether "allowunauthorizedcert" is the desired config key name/schema (no existing convention found elsewhere in this file), and whether config validation/documentation should be added elsewhere to advertise this new option.
🤖 Prompt for AI agents
In meshsms.js around line 153, review and complete this code-review fix: SMS 'url' provider issues plaintext HTTP GET with attacker-influenced query string and rejectUnauthorized disabled.
What the draft fix changed: In `obj.sendSMS` (the `url` provider branch, HTTPS request options), changed the unconditional `rejectUnauthorized: false` to `rejectUnauthorized: (parent.config.sms.allowunauthorizedcert === true) ? false : true`. TLS certificate validation is now enabled by default and only disabled if an operator explicitly sets a new config flag `sms.allowunauthorizedcert: true` in config.json. This requires an explicit opt-in rather than unconditionally weakening TLS. Unverified/left to reviewer: whether "allowunauthorizedcert" is the desired config key name/schema (no existing convention found elsewhere in this file), and whether config validation/documentation should be added elsewhere to advertise this new option.
Verify the change is correct and complete; do not refactor unrelated code.
fix confidence: 🟡 70 medium — react 👍/👎 to teach the reviewer
| @@ -484,8 +484,8 @@ Server.prototype.recvConnectInitial = function (s) { | |||
| break; | |||
| case gcc.MessageType.CS_NET: | |||
There was a problem hiding this comment.
🦩 🟠 Wildcard variable 'i' reused/shadowed across nested loops in gcc.js block-parsing helpers
In Server.prototype.recvConnectInitial, renamed the inner for (var i = 0; ...) loop variable to j (and updated its uses j + 1 + Channel.MCS_GLOBAL_CHANNEL) inside the case gcc.MessageType.CS_NET: block, so it no longer clobbers the outer for(var i in clientSettings) loop variable that continues iterating after this case executes. No other logic changed.
🤖 Prompt for AI agents
In rdp/protocol/t125/mcs.js around line 485, review and complete this code-review fix: Wildcard variable 'i' reused/shadowed across nested loops in gcc.js block-parsing helpers.
What the draft fix changed: In `Server.prototype.recvConnectInitial`, renamed the inner `for (var i = 0; ...)` loop variable to `j` (and updated its uses `j + 1 + Channel.MCS_GLOBAL_CHANNEL`) inside the `case gcc.MessageType.CS_NET:` block, so it no longer clobbers the outer `for(var i in clientSettings)` loop variable that continues iterating after this case executes. No other logic changed.
Verify the change is correct and complete; do not refactor unrelated code.
fix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
| metadata: | ||
| name: {{ .Chart.Name }}-default | ||
| data: | ||
| OPENFRAME_MODE: "true" |
There was a problem hiding this comment.
🦩 🟠 OPENFRAME_GATEWAY_URL hardcoded to 'localhost' in shared Helm configmap template
In charts/meshcentral/templates/configmap.yaml, replaced the hardcoded OPENFRAME_MODE: "true" and OPENFRAME_GATEWAY_URL: "localhost" values in the first ConfigMap's data block with template expressions {{ .Values.openframe.mode | default "true" | quote }} and {{ .Values.openframe.gatewayUrl | quote }}, mirroring the .Values-sourced pattern used elsewhere in the same file (e.g. .Values.config.settings.port, .Values.mongodb.hostsValue). This is unverified because the corresponding openframe.mode / openframe.gatewayUrl keys are not confirmed to exist in values.yaml in this repo snapshot; a complete fix requires adding those keys (with an appropriate default gateway URL per environment) to charts/meshcentral/values.yaml, which is outside this single file's scope.
🤖 Prompt for AI agents
In charts/meshcentral/templates/configmap.yaml around line 6, review and complete this code-review fix: OPENFRAME_GATEWAY_URL hardcoded to 'localhost' in shared Helm configmap template.
What the draft fix changed: In `charts/meshcentral/templates/configmap.yaml`, replaced the hardcoded `OPENFRAME_MODE: "true"` and `OPENFRAME_GATEWAY_URL: "localhost"` values in the first ConfigMap's `data` block with template expressions `{{ .Values.openframe.mode | default "true" | quote }}` and `{{ .Values.openframe.gatewayUrl | quote }}`, mirroring the `.Values`-sourced pattern used elsewhere in the same file (e.g. `.Values.config.settings.port`, `.Values.mongodb.hostsValue`). This is unverified because the corresponding `openframe.mode` / `openframe.gatewayUrl` keys are not confirmed to exist in `values.yaml` in this repo snapshot; a complete fix requires adding those keys (with an appropriate default gateway URL per environment) to `charts/meshcentral/values.yaml`, which is outside this single file's scope.
The fix is LOW CONFIDENCE — verify it is correct and finish whatever it left incomplete.
fix confidence: 🔴 55 low — review closely — react 👍/👎 to teach the reviewer
| @@ -161,7 +161,7 @@ function amt_heci() | |||
|
|
|||
| // Fill the left with zeros until the string is of a given length | |||
| function zeroLeftPad(str, len) { | |||
There was a problem hiding this comment.
🦩 🟠 amt-mei.js zeroLeftPad has broken guard logic due to operator precedence, allowing null length to bypass early return
In zeroLeftPad (agents/modules_meshcmd/amt-mei.js), changed the guard condition on the line if ((len == null) && (typeof (len) != 'number')) { return null; } to use || instead of &&, matching the intended logic stated in the finding: if ((len == null) || (typeof(len) != 'number')) { return null; }. This ensures the function returns null early both when len is null/undefined and when len is any non-numeric value (e.g., a string), preventing the silent NaN-arithmetic loop bug described in the finding. All call sites pass numeric literals for len, so this tightening of the guard does not change behavior for existing callers.
🤖 Prompt for AI agents
In agents/modules_meshcmd/amt-mei.js around line 163, review and complete this code-review fix: amt-mei.js zeroLeftPad has broken guard logic due to operator precedence, allowing null length to bypass early return.
What the draft fix changed: In `zeroLeftPad` (agents/modules_meshcmd/amt-mei.js), changed the guard condition on the line `if ((len == null) && (typeof (len) != 'number')) { return null; }` to use `||` instead of `&&`, matching the intended logic stated in the finding: `if ((len == null) || (typeof(len) != 'number')) { return null; }`. This ensures the function returns null early both when `len` is null/undefined and when `len` is any non-numeric value (e.g., a string), preventing the silent NaN-arithmetic loop bug described in the finding. All call sites pass numeric literals for `len`, so this tightening of the guard does not change behavior for existing callers.
Verify the change is correct and complete; do not refactor unrelated code.
fix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer
| obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected); | ||
| obj.socket.setEncoding('binary'); | ||
| obj.socket.setTimeout(60000); // Set socket idle timeout | ||
| obj.socket.on('error', function (ex) { obj.xtlsMethod = 1 - obj.xtlsMethod; }); |
There was a problem hiding this comment.
🦩 🟠 MPS/CIRA TLS socket 'error' handler toggles xtlsMethod but never retries or surfaces failure, potentially leaving connection silently stuck
In obj.xxConnectHttpSocket (CIRA/MPS TLS branch, obj.cirasocket.onStateChange handler, state==2 case), changed the CIRA-tunneled TLS socket's 'error' handler so it now (a) logs the actual error via console.error('CIRA TLS socket error: ' + ...) for diagnostic trail, (b) still toggles obj.xtlsMethod for the next connection attempt, and (c) calls obj.xxOnSocketClosed() to tear down the socket state, drain/retry the pending AJAX call queue (via the existing retry-with-setTimeout logic in xxOnSocketClosed), and avoid leaving ActiveAjaxCount/PendingAjax stalled. This reuses the existing close/retry machinery rather than inventing a new reconnect path, minimizing risk. Unverified: whether calling xxOnSocketClosed() from within the TLS 'error' event conflicts with the socket's own subsequent 'close' event firing on the same socket (both are still wired since only 'data'/'close'/'timeout' listeners are removed in xxOnSocketClosed, not 'error') — this could cause xxOnSocketClosed to run twice for one failure. A complete fix would additionally guard against double invocation (e.g. via a flag) and confirm this does not cause double-decrement of ActiveAjaxCount or double retry scheduling.
🤖 Prompt for AI agents
In amt/amt-wsman-comm.js around line 254, review and complete this code-review fix: MPS/CIRA TLS socket 'error' handler toggles xtlsMethod but never retries or surfaces failure, potentially leaving connection silently stuck.
What the draft fix changed: In `obj.xxConnectHttpSocket` (CIRA/MPS TLS branch, `obj.cirasocket.onStateChange` handler, state==2 case), changed the CIRA-tunneled TLS socket's `'error'` handler so it now (a) logs the actual error via `console.error('CIRA TLS socket error: ' + ...)` for diagnostic trail, (b) still toggles `obj.xtlsMethod` for the next connection attempt, and (c) calls `obj.xxOnSocketClosed()` to tear down the socket state, drain/retry the pending AJAX call queue (via the existing retry-with-setTimeout logic in `xxOnSocketClosed`), and avoid leaving `ActiveAjaxCount`/`PendingAjax` stalled. This reuses the existing close/retry machinery rather than inventing a new reconnect path, minimizing risk. Unverified: whether calling `xxOnSocketClosed()` from within the TLS 'error' event conflicts with the socket's own subsequent 'close' event firing on the same socket (both are still wired since only 'data'/'close'/'timeout' listeners are removed in xxOnSocketClosed, not 'error') — this could cause `xxOnSocketClosed` to run twice for one failure. A complete fix would additionally guard against double invocation (e.g. via a flag) and confirm this does not cause double-decrement of ActiveAjaxCount or double retry scheduling.
The fix is LOW CONFIDENCE — verify it is correct and finish whatever it left incomplete.
fix confidence: 🔴 55 low — review closely — react 👍/👎 to teach the reviewer
| @@ -226,8 +226,8 @@ module.exports.CreateLetsEncrypt = function (parent) { | |||
| // Save certificate and private key to PEM files | |||
| var certFile = obj.path.join(obj.certPath, (obj.runAsProduction ? 'production.crt' : 'staging.crt')); | |||
There was a problem hiding this comment.
🦩 🟠 letsencrypt.js writes private key material to disk without restricting file permissions
In obj.requestCertificate's obj.client.auto(...).then(...) success callback, both obj.fs.writeFileSync(certFile, cert, ...) and obj.fs.writeFileSync(keyFile, obj.tempPrivateKey, ...) now pass an explicit { mode: 0o600 } option, restricting the created files to owner-only read/write regardless of the process umask. Note: if the file already exists with looser permissions from a prior run, writeFileSync mode only applies at file creation time (open with O_CREAT) and won't retroactively chmod an existing file with different permissions on all platforms/Node versions — a fully complete fix might also explicitly fs.chmodSync(keyFile, 0o600) after writing to cover that edge case, but this was not added to keep the change minimal.
🤖 Prompt for AI agents
In letsencrypt.js around line 227, review and complete this code-review fix: letsencrypt.js writes private key material to disk without restricting file permissions.
What the draft fix changed: In `obj.requestCertificate`'s `obj.client.auto(...).then(...)` success callback, both `obj.fs.writeFileSync(certFile, cert, ...)` and `obj.fs.writeFileSync(keyFile, obj.tempPrivateKey, ...)` now pass an explicit `{ mode: 0o600 }` option, restricting the created files to owner-only read/write regardless of the process umask. Note: if the file already exists with looser permissions from a prior run, `writeFileSync` mode only applies at file creation time (open with O_CREAT) and won't retroactively chmod an existing file with different permissions on all platforms/Node versions — a fully complete fix might also explicitly `fs.chmodSync(keyFile, 0o600)` after writing to cover that edge case, but this was not added to keep the change minimal.
Verify the change is correct and complete; do not refactor unrelated code.
fix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer
| if (obj.onStep) obj.onStep(obj); | ||
| return obj; | ||
| } | ||
| var cmdid = ReadShort(obj.script, obj.ip); |
There was a problem hiding this comment.
🦩 🟠 amtscript.js interprets untrusted script bytecode with unbounded jsonparse/eval-adjacent operations and no bounds checking on cmdlen/argcount
In obj.step() (inside the o.script_setup function, which is currently commented out in the source but is the actual VM step() referenced by the finding), added bounds validation before trusting binary-derived values: (a) a check that obj.ip + 6 does not exceed obj.script.length before reading the 6-byte command header via ReadShort; (b) a check that cmdlen >= 6 and obj.ip + cmdlen <= obj.script.length, which both prevents slice/substring overruns and guarantees obj.ip advances by at least 6 bytes each step, eliminating the zero-length infinite-loop case; (c) a check that argcount is within a sane range (0–10, matching the fixed-size argsval array used later in the function); (d) inside the argument-parsing loop, checks that the 2-byte arglen field and the resulting argument slice (argptr+2+arglen) stay within both the current command's bounds and the overall buffer, with arglen required to be at least 1. On any validation failure the script is halted (obj.state = 9), an error is logged, and obj.stop() is called to prevent further processing of a corrupted/malicious script. NOTE OF RISK: this code path is inside a /* ... */ block comment in the given file (the entire script_setup function, including step(), is dead/commented-out code in the file as provided — it appears to be legacy code retained for reference, with the "live" implementation presumably living elsewhere or in the surrounding, uncommented module). Because the finding's evidence and line 121 reference falls within this commented block, I made the minimal-risk change of fixing the logic in place without uncommenting it, since uncommenting could have broader behavioral/architectural consequences outside the scope of "minimal, safe fix." A complete fix requires confirming whether this commented block is actually dead code or a copy-paste/version artifact of the real runtime VM (if the latter lives in another file not shown here, the same bounds checks must be mirrored there); if this block is truly unused, the fix has no runtime effect at all, which is why confidence is capped at 45.
🤖 Prompt for AI agents
In amtscript.js around line 121, review and complete this code-review fix: amtscript.js interprets untrusted script bytecode with unbounded jsonparse/eval-adjacent operations and no bounds checking on cmdlen/argcount.
What the draft fix changed: In `obj.step()` (inside the `o.script_setup` function, which is currently commented out in the source but is the actual VM step() referenced by the finding), added bounds validation before trusting binary-derived values: (a) a check that `obj.ip + 6` does not exceed `obj.script.length` before reading the 6-byte command header via `ReadShort`; (b) a check that `cmdlen >= 6` and `obj.ip + cmdlen <= obj.script.length`, which both prevents `slice`/`substring` overruns and guarantees `obj.ip` advances by at least 6 bytes each step, eliminating the zero-length infinite-loop case; (c) a check that `argcount` is within a sane range (0–10, matching the fixed-size `argsval` array used later in the function); (d) inside the argument-parsing loop, checks that the 2-byte `arglen` field and the resulting argument slice (`argptr+2+arglen`) stay within both the current command's bounds and the overall buffer, with `arglen` required to be at least 1. On any validation failure the script is halted (`obj.state = 9`), an error is logged, and `obj.stop()` is called to prevent further processing of a corrupted/malicious script. NOTE OF RISK: this code path is inside a `/* ... */` block comment in the given file (the entire `script_setup` function, including `step()`, is dead/commented-out code in the file as provided — it appears to be legacy code retained for reference, with the "live" implementation presumably living elsewhere or in the surrounding, uncommented module). Because the finding's evidence and line 121 reference falls within this commented block, I made the minimal-risk change of fixing the logic in place without uncommenting it, since uncommenting could have broader behavioral/architectural consequences outside the scope of "minimal, safe fix." A complete fix requires confirming whether this commented block is actually dead code or a copy-paste/version artifact of the real runtime VM (if the latter lives in another file not shown here, the same bounds checks must be mirrored there); if this block is truly unused, the fix has no runtime effect at all, which is why confidence is capped at 45.
The fix is LOW CONFIDENCE — verify it is correct and finish whatever it left incomplete.
fix confidence: 🔴 45 low — review closely — react 👍/👎 to teach the reviewer
| sha256.write('bob'); | ||
| sha256.end(); | ||
| } | ||
| { |
There was a problem hiding this comment.
🦩 🔵 agents/testsuite.js contains a duplicate/dead SHA256Stream test block explicitly marked FAIL
Removed the duplicate, known-broken SHA256Stream test block (the sha256x block prefixed with // FAIL!!!!!!!!!) located immediately after the first "Test 1: SHA256 hashing" block. The original, working test block using sha256 remains untouched; only the dead/duplicate block and its blank separator line were deleted.
🤖 Prompt for AI agents
In agents/testsuite.js around line 113, review and complete this code-review fix: agents/testsuite.js contains a duplicate/dead SHA256Stream test block explicitly marked FAIL.
What the draft fix changed: Removed the duplicate, known-broken SHA256Stream test block (the `sha256x` block prefixed with `// FAIL!!!!!!!!!`) located immediately after the first "Test 1: SHA256 hashing" block. The original, working test block using `sha256` remains untouched; only the dead/duplicate block and its blank separator line were deleted.
Verify the change is correct and complete; do not refactor unrelated code.
fix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer
Closes 16 review findings across 16 files.
Draft — this is a starting point, not a finished change. The fix required judgment, so read it before trusting it.
Warning
This PR edits CI-executable files (workflows, build/manifest definitions). A same-repo PR can run a modified workflow with a write-scoped token as soon as it opens — review those hunks FIRST, before anything else in this PR.
agents/modules_meshcmd/amt-apfclient.js:147agents/modules_meshcore/computer-identifiers.js:92meshsms.js:153rdp/protocol/t125/mcs.js:485charts/meshcentral/templates/configmap.yaml:6public/js/ui-components.js:212public/mstsc/keyboard.js:229public/scripts/amt-wsman-ws-0.2.0.js:25.github/workflows/sync-upstream.yml:30rdp/core/type.js:305agents/meshcore_diagnostic.js:87agents/modules_meshcmd/amt-mei.js:163amt/amt-wsman-comm.js:254letsencrypt.js:227amtscript.js:121agents/testsuite.js:113What changed — and what was deliberately left — is explained per finding as inline review comments on the lines each finding touched.
Run: https://product-hub.flamingo.so/admin/code-review
Run id:
a271a374-6235-40d2-8fdd-b6d4de951f4bMerging this PR is recorded as acceptance of the rule that produced it;
closing it unmerged is recorded as rejection. Both feed rule health, so
closing a wrong suggestion is useful rather than merely tidy.
ClickUp task: CU-86akdypw4 Ad hoc sweep fixes across services (14 PRs)