Turning a PlantUML diagram into ready-to-compile Java classes is the kind of chore a developer is happy to hand to an extension. Lolpaon.umltocode has done that job since February 2026 under the Marketplace name UML to Code. It has 326 installs, two ratings, both five stars, and a description that talks about diagram parsing and nothing else.

The listing started changing on October 22. Between then and October 26 the publisher shipped seven public builds. The changelog frames the work as performance and context quality: faster completions, PDF awareness, a cleaner command palette. The 1.0.0 entry is more direct. It announces “complete obfuscation of network addresses” and states that no IP address or hostname appears in plaintext in the source.

The extension also runs an inline completion provider that is enabled by default and covers every file in the editor. Each debounced keystroke sends the file path, the surrounding source, the compiler diagnostics, other files in the folder, and text pulled from PDFs to a host the code hides. Certificate validation is disabled on that channel. In the current build the status-bar indicator is gone, so nothing on screen shows that the channel is open.

TL;DR

  • The extension is still listed. Lolpaon.umltocode 1.0.5 was the current build on October 26, 2026 at 21:37 UTC.
  • The completion provider is on by default. It registers for **, file, and untitled on onStartupFinished, with a 150 ms debounce and no consent prompt.
  • Each request carries more than the current line. The payload includes the absolute file path, up to 7,000 characters before the cursor, 2,000 after it, the file’s diagnostics, nearby comments, folder diagrams, up to four neighbouring code files, and up to three extracted PDFs.
  • The destination is deliberately hidden. Hostnames and addresses are stored in a base64 string table and XOR-decoded with 0x42 at runtime.
  • The hidden destination is a legitimate AI gateway. The hardcoded addresses resolve to api.kilo.ai and ai-gateway.kilo.ai, a third-party inference service. The extension authenticates with the literal token anonymous.
  • TLS verification is off on every hop. rejectUnauthorized: false appears on completion requests, on DNS-over-HTTPS lookups, and inside proxy tunnels. Nothing in the channel proves the peer is Kilo.
  • There is no shell, no persistence, and no second stage. The extension is not a dropper. The problem is what it sends, where it sends it, and the channel it uses.

Timeline

Version Published (UTC) Change
0.0.4 2026-02-05 18:44 Last build before an eight-month gap. Diagram parsing and Java generation only.
0.0.5 2026-10-22 23:39 Multi-language generators and SQL output return.
0.0.6 2026-10-23 12:18 Not described in the changelog.
0.0.7 2026-10-23 12:18 Activates on onStartupFinished. Completion client present.
0.0.8 2026-10-26 14:16 Folder context: Draw.io and PlantUML scanning, schema awareness.
0.0.9 2026-10-26 18:33 DNS-over-HTTPS, proxy tunnelling, privateBrowsing setting, default on.
1.0.0 2026-10-26 18:48 Endpoint strings obfuscated. Default server becomes "auto".
1.0.5 2026-10-26 21:37 Status-bar indicator and connection-test command removed.

The changelog also documents 1.0.1 through 1.0.4 on October 26. Those four builds do not appear in the public version list. The 1.0.1 entry describes a “payload format” correction, and the 1.0.5 entry describes “maximum discretion”: the diagnostic commands and the status-bar button were removed, and the completion engine now runs “100% silently” in the background.

Why the vector works

Two expectations do the work.

A UML tool is expected to read the project folder. It opens .puml and .drawio files, parses them, and writes generated source next to the diagram. Scanning a directory for diagrams is the advertised function, so a folder read does not look anomalous.

An autocomplete engine is expected to read the current file, and often the files around it. Sending code to a model is what completion extensions do. The question a user should ask is where the model runs, and the listing does not answer that question.

The permission model cannot answer it either. A VS Code extension runs in the extension host with the same file and network access as the editor. The Marketplace can express that an extension executes code, and the manifest for this one sets Microsoft.VisualStudio.Code.ExecutesCode to true. It cannot express “may parse diagrams but may not transmit source.”

The publisher carries the Marketplace “verified” flag. That flag confirms a verified account, not a code review, and the publisher has no verified domain.

Activation

The manifest declares onStartupFinished alongside the language activation events. The provider is registered before the user runs any command:

const completionRegistration = vscode.languages.registerInlineCompletionItemProvider([
    { pattern: '**' },
    { scheme: 'file' },
    { scheme: 'untitled' }
], inlineProvider);

getConfig() reads umltocode.completion.enabled with a default of true. There is no first-run dialog, no notification, and no entry in the command palette that mentions completion. The only visible command is the UML conversion.

Collection

extractContext() assembles the request payload. The file path is absolute, so the project name and directory structure are included:

sections.push(`Source File: ${context.filePath} (${context.languageId})`);
sections.push(`[Code before cursor]:\n${context.prefix.slice(-7000)}`);
sections.push(`[Code after cursor]:\n${context.suffix.slice(0, 2000)}`);

Before that, the function collects every Error and Warning diagnostic for the file and every comment in the twelve lines above the cursor. Diagnostics expose internal paths, type names, and sometimes secrets in error messages. Comments carry the developer’s working notes.

For saved files, FolderContext.getFolderContext() then runs fs.readdirSync on the containing directory and adds three more classes of content:

  • Diagrams. .drawio, .xml, .puml, .plantuml, and .uml files are parsed and reduced to table and relation summaries.
  • Neighbouring code. Up to four files with .js, .ts, .py, .java, .cs, .cpp, or .sql extensions are ranked by filename proximity, by theory and example keywords, and by shared strings. The first 1,600 characters of each are included.
  • PDFs. Up to three PDFs are read from the same folder, decompressed with a pure-JavaScript FlateDecode parser, and reduced to 4,000 characters of text each.

The PDF parser is the clearest statement of intent. A diagram converter has no reason to implement a PDF text extractor. The changelog names the target directly: it detects a matching assignment brief such as 205_Epee.pdf next to 205_Epee.js, plus course theory PDFs, and feeds their coordinates and formulas into the completion. Other entries reference exercise files by name, including 203_SabreLaser.js, 204_Fleche.js, and a set of p5.js vector-graphics labs.

The folder scan is cached per document for 15 seconds, so it does not repeat on every keystroke. The document text still goes out on every request.

Endpoint resolution

The addresses are not visible in the source. A decoder runs at load time:

const _dec = (b64, key = 0x42) => { /* base64 → binary → XOR each byte */ };

const REMOTE_GATEWAY_HOST = _dec('IytvJSM2JzUjO2wpKy4tbCMr');            // "ai-gateway.kilo.ai"
const DEFAULT_SERVER_ADDR = _dec('KjY2MjF4bW10dmxwcXtsc3BxbHNwe3h2dnFtIzIrbTRz');
                                                                         // "https://64.239.123.129:443/api/v1"
const KNOWN_GATEWAY_IPS = [
    _dec('dHZscHF7bHNwcWxzcHs='),  // "64.239.123.129"
    _dec('dHZscHF7bHNye2xzcHs='),  // "64.239.109.129"
    _dec('dHZscHF7bHNwcWx0dw=='),  // "64.239.123.65"
    _dec('dHZscHF7bHNye2x0dw=='),  // "64.239.109.65"
];

When the configured address is "auto", which is the default, the client uses the raw IP. It then injects the gateway hostname as the HTTP Host header and as the TLS server name:

const isIp = /^(\d{1,3}\.){3}\d{1,3}$/.test(parsed.hostname);
if (isIp && !finalHeaders['Host']) {
    finalHeaders['Host'] = REMOTE_GATEWAY_HOST;
}
const options = {
    hostname: parsed.hostname,
    rejectUnauthorized: false,
    servername: isIp ? REMOTE_GATEWAY_HOST : parsed.hostname,
};

The header injection is how a client reaches a virtual host by address. In a normal client this is a fallback. Here it is the default path, and it means the visible request in any proxy log is an IP address, not a domain.

buildCandidateEndpoints() layers a cached endpoint, the configured endpoint, addresses resolved through DNS-over-HTTPS against 1.1.1.1 and dns.google, the four static addresses, and the domain form. It tries each one until a response parses as JSON.

The channel is not authenticated

rejectUnauthorized: false appears in the completion request, in the DoH resolver, and in the TLS socket created inside an HTTP CONNECT tunnel. Certificate validation is disabled in all three places, so the client accepts any certificate from any peer.

An inline completion provider returns text that the editor renders as ghost text. The developer accepts it with Tab. If the network can terminate the TLS session, the network can choose that text. On a campus or hotel network, that is the same network the extension is built to traverse: the client detects captive portals and block pages, retries around them, and tunnels through the configured proxy.

The response is parsed for choices[0].message.content, choices[0].text, response, or text, stripped of Markdown fences, and inserted. There is no signature check, no allowlist, and no prompt to review where the suggestion came from.

Where the data goes

The hardcoded addresses are not a private command server. Public DNS returns them for api.kilo.ai and ai-gateway.kilo.ai, and the model identifiers in the string table, kilo-auto/small and kilo-auto/balanced, are Kilo model names. Kilo is a legitimate AI gateway. The extension is a client for it.

That changes the finding but does not resolve it. The extension sends source code, file paths, diagnostics, folder contents, and course documents to a third party by default, and it does so under a destination the code actively hides. The listing did not disclose the transmission until 1.0.5, and the 1.0.5 disclosure is a French section in the README that describes “contextual completion” and “optimized network resolution” without naming the service. The setting text calls "auto" a “secure cloud gateway,” not Kilo.

The Authorization header is Bearer anonymous unless the user sets an API key. So the traffic is either unauthenticated use of a third-party service or the operator’s own account. Nothing in the package distinguishes the two.

The concealment itself is the finding. Version 1.0.0 states that no IP address or hostname appears in plaintext. The 0.0.7 build carried a source comment that called the raw-IP path “Stealth IP”; the current build calls the same code “Private Browsing mode” and “stealth headers.” The function names were cleaned. The transport was not.

Configuration as an attack path

umltocode.completion.serverAddress has no configuration scope in the manifest, so it can be set at the workspace level. A repository that ships .vscode/settings.json can point the completion client at any host. If the developer has configured an API key at the user level, that repository receives both the key and the code context.

This is a capability, not an observed behaviour. It is also the same trust boundary that makes the hidden default a problem: the destination is configuration, and configuration travels with the code.

Why it survives review

Assumption Behaviour
A UML tool does not touch the network. It ships an HTTP and TLS client with proxy support and multi-endpoint fallback.
The listing describes what the extension does. The listing describes diagram parsing. Completion appears only in the 1.0.5 README, in French, without naming the destination.
A hostname in the code shows where data goes. Hostnames and addresses are base64 and XOR encoded. The visible default is a raw IP.
TLS proves the destination. Certificate validation is disabled on completion, DoH, and proxied connections.
A visible indicator shows when it runs. The status-bar toggle existed in 1.0.0 and was removed in 1.0.5.
Installing is the security boundary. The extension host has full file and network access, and the permission model cannot narrow it.

Outlook

AI completion is being added to extensions that have no other reason to make network requests. A diagram converter, a formatter, a theme preview, and a note-taking tool can all justify a model call in the same way this one does.

Expect three patterns to continue:

  • Endpoint concealment will become normal. Vendors and hobbyists both have reasons to avoid a visible domain, from filter evasion to hiding which provider they resell. Encoded endpoints make review harder for everyone.
  • Context collection will expand. The useful autocomplete reads more than the current file. Neighbouring files, PDFs, terminals, and repository history are the obvious next inputs, and each one widens the blast radius of an undisclosed channel.
  • Configuration will become an attack path. A setting that points a client at a host is a target for repositories, dotfiles, and onboarding scripts. Workspace-level settings that name a network destination deserve the same scrutiny as build scripts.

The economics favour this. A working AI feature makes an extension useful, and the transport details are rarely reviewed.

Remediation

Developers who installed Lolpaon.umltocode should set umltocode.completion.enabled to false or remove the extension. Any secret that was open in the editor while the extension ran, including keys in .env files, config files, and untitled buffers, should be treated as disclosed and rotated. The same applies to PDFs and source files in the folder of any file that was edited while the extension ran.

For reviewers, the useful checks are concrete. Read the activation events, not just the commands. Follow every network call to its decoded destination. Check whether TLS verification is enabled. Check whether the destination is user-configurable and at what scope. Check what the extension reads before it writes.

The general principle is narrower than “audit your extensions.” An inline completion provider is a code-injection surface. It renders text from a remote host directly into the editor, and the developer accepts it with a single keypress. That surface must not accept text from a host the client cannot authenticate.

Indicators

Extension

  • Extension ID: Lolpaon.umltocode Pkgscan Result
  • Display name: UML to Code
  • Publisher: Lolpaon
  • Publisher account ID: 0c0e03e0-a4ab-4e6c-a47f-b9f5bbdc5b95
  • Extension ID (GUID): 23f79fd6-eb85-4cdf-8312-aaf680b1af77
  • Published: 2026-02-03
  • Installs at analysis: 326 installs, 397 downloads, 74 updates, 2 ratings

Builds

Version Published (UTC) Size SHA-256
0.0.7 2026-10-23 12:18 61,467 B dec386060981aca01fd359a8e83e22e44abe4967805db53ef09c088694b2b4f3 Pkgscan Result
1.0.0 2026-10-26 18:48 78,266 B f314bc708474f96f4f17a5f4f5b3adbd4cab9cf547b4e6aa218709c351c22bde Pkgscan Result
1.0.5 2026-10-26 21:37 81,820 B 8ad550d363270e84b47bd03c0027e2a46c5b39ef27c6bb6150b642735ea193da

Network

  • 64.239.123[.]129
  • 64.239.109[.]129
  • 64.239.123[.]65
  • 64.239.109[.]65
  • Gateway host used in the Host header and TLS server name: ai-gateway.kilo.ai
  • Default endpoint: https[://]64.239.123[.]129/api/v1/chat/completions
  • DNS-over-HTTPS resolvers: 1.1.1.1, dns.google

Kilo is a legitimate AI gateway, and the addresses above are its published resolution. Do not block or flag the parent domain or the provider broadly. The indicator is an application using those addresses from a VS Code extension, with a hardcoded IP, an injected Host header, and certificate validation disabled.

Behavioural

  • Inline completion provider registered for ** with no visible command or status item.
  • umltocode.completion.enabled defaulting to true, debounceMs defaulting to 150.
  • Authorization: Bearer anonymous.
  • User-Agent set to Chrome 131 with fr-CA language headers, Sec-GPC: 1, and DNT: 1.
  • Request bodies containing [Code before cursor], [Code after cursor], [Active File Diagnostics], [Context from Folder, Diagrams & PDF Instructions], and [Instructions & Consignes extraites du PDF "…"].
  • Directory reads of .puml, .plantuml, .drawio, .uml, and .pdf files next to the active document.
  • rejectUnauthorized: false combined with a raw-IP default and a Host override.

File

  • Icon SHA-256: 983df60201cfbc78ebcaf57552352516996bd96385cb32fbe04411497853ff36