{
  "version": "2",
  "id": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c",
  "title": "Developer traces Microsoft trojan flag on a Tauri app to a statically linked crypto library",
  "summary": "A community write-up describes bisecting release builds against VirusTotal to find that adding sodiumoxide, the Rust binding to libsodium, coincided with a Microsoft machine-learning detection, and swapping it for pure-Rust crates while keeping existing encrypted files readable.",
  "body": "A developer maintaining an open-source Windows desktop application has published an account of tracking down why Microsoft's antivirus engine began flagging a release binary as a trojan, and of replacing the component he identified as the trigger. The post, published on dev.to, describes the app as Roblox Account Manager, a GPL-3.0 project built with Rust, Tauri 2 and React that manages multiple Roblox accounts, launches several game clients at once and automates actions such as rejoining servers. The author states the investigation, numbers and code come from the project's history, and that the post was written with the help of an AI assistant.\n\nAccording to the author, after one release Windows began warning users about the executable, and a VirusTotal scan showed 1 out of 75 engines reporting it: Microsoft, with the label Trojan:Win32/Wacatac.B!ml. He emphasises the !ml suffix, describing it as a machine-learning verdict rather than a signature match, meaning no known malware pattern was found and a model judged the binary to resemble malware. That distinction matters for anyone shipping unsigned desktop software, because it means there is no specific byte string to remove.\n\nThe author's first attempt at diagnosis compared the last clean build with the flagged one using pefile. He reports that both binaries had the same 16 heuristically heavy imports, including SendInput, SetForegroundWindow, TerminateProcess, DuplicateHandle and RegSetValueExW, the same section entropy and the same linker, with only four new imports that he characterises as boring: FlushFileBuffers, OpenEventW, RemoveDirectoryW and MessageBoxW. He concluded at the time that the behaviour profile was identical and the model had simply changed its mind, and he states plainly that this conclusion was wrong, because the import table is not everything the model examines.\n\nHe then switched to bisection. The method he describes uses the VirusTotal API, which he says is free at 4 requests per minute and 500 per day, with a small script that hashes the file, looks up an existing report and uploads only if the hash is new. He then used git bisect, building the release executable at each step and scanning it, marking each build good at 0 out of 75 or bad at 1 out of 75. Each step took a few minutes, mostly consumed by the Rust release build. He notes a practical trap: his first attempt bisected by date and broke on merge commits, and he advises using real git bisect instead.\n\nAccording to the author, the commit that first triggered the problem was the change adding account-file encryption through sodiumoxide, which provides Rust bindings for libsodium. His account is that sodiumoxide links the libsodium C library statically into the binary, and that having a chunk of optimised native crypto primitives inside an application that also keeps credentials and injects input looks sufficiently like the payloads ransomware and stealers distribute for the classifier to trigger. He makes clear that libsodium itself is not at fault, describing the issue instead as the particular combination the classifier has learned to treat as suspicious.\n\nThe replacement had a hard constraint: users already had encrypted files on disk, some protected with a password, so the new implementation had to read those files byte for byte. The author says he chose pure-Rust RustCrypto crates and matched every libsodium parameter, listing argon2 0.6, crypto_secretbox 0.1 for XSalsa20-Poly1305, and sha2 0.10 for the SHA-512 of the password. He documents the derivation parameters as Argon2i version 0x13 with t_cost 6, m_cost 128 MiB, parallelism 1 and a 32-byte output, matching libsodium's crypto_pwhash with the moderate ops and memory limits. He also notes that RustCrypto's crypto_secretbox already produces libsodium's layout, with the 16-byte Poly1305 MAC in front of the ciphertext.\n\nWhat he says gave him confidence in the swap was a compatibility test rather than reasoning about parameters. Before removing libsodium, he encrypted a small payload with the old implementation, pasted the resulting bytes into a test as hex, and required the new code to decrypt them exactly. His stated rule is that if that fixture test fails, existing users' files would be locked, so it must never be relaxed. He points out that an off-by-one in the Argon2 version, memory cost or MAC position is precisely the class of bug that locks users out of their own data.\n\nIn development, the replacement introduced a performance penalty. Per the author, a debug build using pure-Rust Argon2 required 5.4 seconds for each derivation, versus 0.3 seconds when optimised; libsodium had never shown such a slowdown, since it arrives as precompiled C. Because cargo test executes in debug mode, the suite's runtime grew from minutes to taking up most of the CI time. To fix this, he uses one Cargo.toml block that applies optimisation to dependencies even during dev builds, with an opt-level of 3 for all dev packages, while his own crate stays unoptimised and therefore still compiles fast. The Rust suite, he reports, fell from 230 seconds to 41 seconds, which he notes is quicker than it had been under libsodium.\n\nThe author also states what he did not do: he did not add code signing, and he did not remove the encryption. He describes code signing as the durable fix and mentions free options for open-source projects such as the SignPath Foundation, but says that until signing is in place, measuring every release and keeping the recommended download clean is what he can do.\n\nThe post closes with a set of lessons framed as advice for anyone shipping an unsigned desktop app. He argues that !ml verdicts are a moving target, noting that the same source built with different feature flags received different verdicts from the same model, so a clean result cannot be won once and must be measured every release. He also reports that VirusTotal's Microsoft engine and local Defender disagreed, with a build passing local MpCmdRun and still being flagged on VirusTotal, which is why he says both should be checked since users see both.\n\nRather than cryptography, two of his lessons concern process. He says that although his meticulous import-table analysis led him to the wrong conclusion, a few rounds of git bisect identified the correct commit, and his advice is to bisect instead of theorising. A scanner failure is also described: on one occasion his scan script reported everything clean even though the Defender step had crashed, which he puts down to a UTF-8 em dash inside a BOM-less PowerShell script breaking Windows PowerShell 5.1, while the VirusTotal step returned exit code 0 regardless of the verdict. His principle is that a scanner that failed to run can never be counted as clean, and he notes that this principle is now covered by a test.\n\nFor developers shipping desktop software, the practical takeaway is procedural rather than cryptographic. The account describes a workflow in which each release is built, hashed, checked against an existing VirusTotal report or uploaded, and recorded as clean or flagged, with git bisect used to localise a regression to a single commit when a verdict changes. That is a repeatable way to answer the question of which change coincided with a detection, and it is cheap in the sense that the API tier described is free, though it costs build time per bisection step. The author's own experience suggests the answer may not be in the obvious static features: identical imports, entropy and linker did not explain the change.\n\nThe tradeoffs the account surfaces are worth stating plainly. Replacing a statically linked native crypto library with pure-Rust equivalents removes one heuristic signal but adds a compatibility obligation that must be proven with fixtures, and it can slow debug builds enough to distort CI timings unless dependency optimisation is configured. Keeping the old format readable is non-negotiable when users hold encrypted data, and the author's fixture test is the mechanism he used to enforce that. None of this addresses the underlying classifier behaviour, which he describes as unstable across builds of the same source.\n\nSeveral things remain unknown from the supplied material. The post does not state which release or date the flagging began, how many users were affected, or whether the replacement library eliminated the detection in subsequent releases; it also does not report whether the project has since obtained code signing. The claim that sodiumoxide's statically linked libsodium caused the verdict is the author's own inference from bisection, not an independently verified finding about how Microsoft's model works, and the post itself notes that the same source with different feature flags produced different verdicts. The linked RustCrypto organisation page confirms that pure-Rust implementations of argon2, sha2 and related primitives exist and are maintained, but it does not corroborate the detection story or the compatibility claims.\n\nThe most transferable point for freelancers and small teams shipping unsigned desktop apps is that antivirus verdicts on machine-learning engines are not stable properties of a codebase. The author's account suggests treating them as something to measure per release, to localise by bisection when they change, and to distinguish from signature detections, while recognising that the only durable remedy he names, code signing, is a separate piece of work he had not yet done.",
  "category": "dev",
  "language": "en",
  "datePublished": "2026-09-30T17:17:44.235Z",
  "dateModified": "2026-09-30T17:17:44.235Z",
  "eventDate": null,
  "sourcePublicationDate": "2026-09-30T16:57:50.000Z",
  "source": {
    "name": "dev.to",
    "url": "https://dev.to/luanmacea/how-a-crypto-library-got-my-tauri-app-flagged-as-a-trojan-and-how-i-found-it-by-bisection-11fl",
    "kind": "community"
  },
  "practicalImpact": "For developers shipping unsigned desktop binaries, the account describes a concrete release-time practice: hash each build, check or upload it to VirusTotal, record the verdict, and use git bisect to localise a change in detection to a single commit rather than reasoning from static features. It also flags a CI pitfall, that a scanner which fails to run must not be treated as a clean result, and a compatibility obligation, that swapping a crypto implementation requires fixture tests proving old encrypted data still decrypts.",
  "limitations": "This is a single developer's community post about one project; the causal link between sodiumoxide's statically linked libsodium and the Microsoft verdict is his inference from bisection, not an independently verified explanation of the classifier. The post does not state the release date or version where flagging began, how many users were affected, whether later releases stayed clean, or whether code signing was subsequently adopted. The linked RustCrypto page confirms the existence of pure-Rust crypto crates but does not corroborate the detection or compatibility claims. No pricing, support commitments or independent testing are provided.",
  "keyPoints": [
    "A developer reports that a Microsoft machine-learning detection, labelled Trojan:Win32/Wacatac.B!ml, appeared on one release of an open-source Rust and Tauri Windows app and was traced by git bisect to the commit that added sodiumoxide for account-file encryption.",
    "The author says static analysis of imports, section entropy and linker showed no meaningful difference between the clean and flagged builds, and that his initial conclusion that the model had changed its mind was wrong.",
    "The replacement uses pure-Rust RustCrypto crates with parameters matched to libsodium, validated by a fixture test that decrypts bytes produced by the old implementation so existing user files stay readable.",
    "He reports debug-build Argon2 slowing to 5.4 seconds per derivation versus 0.3 seconds optimised, fixed by optimising dependencies in dev builds, which brought the test suite from 230 seconds to 41 seconds.",
    "The author states he did not add code signing or remove encryption, and advises measuring every release because the same source with different feature flags received different verdicts."
  ],
  "review": {
    "status": "source-reviewed",
    "checkedAt": "2026-09-30T17:17:44.235Z",
    "method": "Automated comparison against retrieved source text; not independent fact-checking.",
    "correctionNote": null
  },
  "sources": [
    {
      "id": 1,
      "url": "https://dev.to/luanmacea/how-a-crypto-library-got-my-tauri-app-flagged-as-a-trojan-and-how-i-found-it-by-bisection-11fl",
      "publisher": "dev.to",
      "title": "How a crypto library got my Tauri app flagged as a trojan (and how I found it by bisection)",
      "publishedAt": 1790787470000,
      "fetchedAt": 1790788643388,
      "hash": "ca64e796de71635d6fa41df46d90bde833278b7633c83a57fd2bb94a4609591b",
      "kind": "community"
    },
    {
      "id": 2,
      "url": "https://github.com/RustCrypto",
      "publisher": "github.com",
      "title": "Rust Crypto",
      "publishedAt": null,
      "fetchedAt": 1790788645202,
      "hash": "3e5a270e6d8821d3f4d038cad62740423447fb0847a67b132c4c540304a448eb",
      "kind": "repository"
    }
  ],
  "claims": [
    {
      "claim": "A VirusTotal scan of the flagged build showed 1 of 75 engines reporting it, with Microsoft labelling it Trojan:Win32/Wacatac.B!ml, which the author describes as a machine-learning verdict rather than a signature match.",
      "source": 1,
      "id": "claim-1",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-1"
    },
    {
      "claim": "The author says the clean and flagged builds shared the same 16 heuristically heavy imports, section entropy and linker, with only four new imports, and that his conclusion that nothing needed fixing was wrong.",
      "source": 1,
      "id": "claim-2",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-2"
    },
    {
      "claim": "He localised the change using git bisect against VirusTotal scans, building the release executable at each step and marking it good at 0 of 75 or bad at 1 of 75, and the first bad commit was the one integrating sodiumoxide for account-file encryption.",
      "source": 1,
      "id": "claim-3",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-3"
    },
    {
      "claim": "The replacement used pure-Rust RustCrypto crates with parameters matched to libsodium, including Argon2i version 0x13 with t_cost 6, m_cost 128 MiB and parallelism 1.",
      "source": 1,
      "id": "claim-4",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-4"
    },
    {
      "claim": "A fixture test requires the new code to decrypt bytes produced by the old libsodium implementation, because a failure there would lock existing users out of their files.",
      "source": 1,
      "id": "claim-5",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-5"
    },
    {
      "claim": "The author reports that pure-Rust Argon2 took 5.4 seconds per derivation in debug builds versus 0.3 seconds optimised, and that optimising dependencies in dev builds cut the test suite from 230 seconds to 41 seconds.",
      "source": 1,
      "id": "claim-6",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-6"
    },
    {
      "claim": "He states he did not add code signing or remove encryption, and that the same source built with different feature flags received different verdicts from the same model.",
      "source": 1,
      "id": "claim-7",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-7"
    },
    {
      "claim": "The RustCrypto project maintains pure-Rust implementations of cryptographic algorithms including argon2, sha2 and chacha20poly1305.",
      "source": 2,
      "id": "claim-8",
      "url": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c#claim-8"
    }
  ],
  "formats": {
    "html": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c",
    "markdown": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c.md",
    "json": "https://freelancenews.online/news/developer-traces-microsoft-trojan-flag-on-a-tauri-app-to-a-statically-ed689e2c.json"
  }
}