Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b925272
refactor: Pin filing-cabinet typescript to stabilize lockfile regener…
mtrezza Jul 13, 2026
44ef273
refactor: Bump undici from 6.24.0 to 6.27.0 (#3381)
dependabot[bot] Jul 13, 2026
de06161
refactor: Bump form-data from 2.5.5 to 2.5.6 (#3375)
dependabot[bot] Jul 13, 2026
bd41672
refactor: Bump @babel/core from 7.29.0 to 7.29.6 (#3402)
mtrezza Jul 13, 2026
9fe8bc7
refactor: Bump ws from 8.20.0 to 8.21.0 (#3403)
mtrezza Jul 13, 2026
4fc1c0b
refactor: Bump parse-server from 9.7.0 to 9.9.0 (#3401)
mtrezza Jul 13, 2026
dffc726
refactor: Bump @protobufjs/utf8 from 1.1.0 to 1.1.2 (#3358)
dependabot[bot] Jul 13, 2026
f0285a5
refactor: Bump postcss from 8.5.3 to 8.5.19 (#3355)
dependabot[bot] Jul 13, 2026
5a8bdce
refactor: Bump fast-uri from 3.1.0 to 3.1.3 (#3353)
dependabot[bot] Jul 13, 2026
72a8478
refactor: Bump fast-xml-builder from 1.1.4 to 1.3.0 (#3352)
dependabot[bot] Jul 13, 2026
d9292db
refactor: Bump fast-xml-parser from 5.5.9 to 5.10.0 (#3346)
dependabot[bot] Jul 13, 2026
f4f751d
refactor: Bump typescript from 6.0.2 to 6.0.3 (#3404)
mtrezza Jul 13, 2026
156ec26
refactor: Bump globals from 17.3.0 to 17.5.0 (#3405)
mtrezza Jul 13, 2026
d922d96
refactor: Bump @babel/plugin-transform-modules-systemjs from 7.29.0 t…
dependabot[bot] Jul 13, 2026
b7f04e1
refactor: Bump webpack from 5.105.1 to 5.106.2 (#3406)
mtrezza Jul 13, 2026
dff103a
refactor: Bump qs from 6.15.0 to 6.15.3 (#3366)
dependabot[bot] Jul 13, 2026
0f9194a
refactor: Bump js-cookie from 3.0.5 to 3.0.8 (#3364)
dependabot[bot] Jul 13, 2026
33ce7b8
fix: Advertise localhost instead of 0.0.0.0 in the dashboard startup …
mtrezza Jul 17, 2026
baeaf8d
chore(release): 9.2.1-alpha.1 [skip ci]
semantic-release-bot Jul 17, 2026
c3e16c2
feat: React 19 + react-router 7 + GraphiQL 5 coordinated upgrade (#3409)
mtrezza Jul 18, 2026
62b4f6f
chore(release): 9.3.0-alpha.1 [skip ci]
semantic-release-bot Jul 18, 2026
7d6408f
refactor: Bump parse-server from 9.9.0 to 9.10.0 (#3411)
mtrezza Jul 19, 2026
5093c77
refactor: Bump jest-environment-jsdom from 30.0.5 to 30.4.1 (#3417)
mtrezza Jul 20, 2026
00b111d
refactor: Bump semver from 7.7.4 to 7.8.5 (#3418)
mtrezza Jul 20, 2026
f52c746
refactor: Bump globals from 17.5.0 to 17.7.0 (#3421)
mtrezza Jul 21, 2026
5b4f2e6
refactor: Bump webpack-cli from 7.0.2 to 7.2.1 (#3423)
mtrezza Jul 23, 2026
7150acf
fix: Prevent Config table crash when a parameter value is null (#3424)
Moumouls Jul 23, 2026
260481a
chore(release): 9.3.0-alpha.2 [skip ci]
semantic-release-bot Jul 23, 2026
2d1b739
fix: Add Cancel and Add buttons to the add-row modal (#1995) (#3383)
dblythy Jul 25, 2026
5d49f1c
chore(release): 9.3.0-alpha.3 [skip ci]
semantic-release-bot Jul 25, 2026
4879959
fix: Info panel data not loaded when opened over an existing selectio…
dblythy Jul 25, 2026
1948f96
chore(release): 9.3.0-alpha.4 [skip ci]
semantic-release-bot Jul 25, 2026
60cb379
fix: Remote access restrictions not enforced when running behind a re…
mtrezza Aug 11, 2026
4b257f9
chore(release): 9.3.0-alpha.5 [skip ci]
semantic-release-bot Aug 11, 2026
33a37e4
refactor: Bump react-router from 7.18.1 to 7.18.2 (#3438)
mtrezza Aug 26, 2026
84c2d81
empty commit to trigger CI
github-actions[bot] Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ test_logs
# AI tools
.claude
.superpowers

# Docs
/docs/
63 changes: 53 additions & 10 deletions Parse-Dashboard/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,52 @@ module.exports = function(config, options) {
cookieSessionStore: options.cookieSessionStore
});

// Headers set by reverse proxies to describe the original client. Their
// presence means the request was relayed and did not originate on this host.
// The `x-forwarded-` prefix is matched as a whole because proxies vary in
// which of them they set, and any single one is enough to reveal a relay.
const forwardingHeaderPrefix = 'x-forwarded-';
const forwardingHeaders = ['x-real-ip', 'forwarded'];

/**
* Checks whether a request is from localhost.
* Checks whether the connection was opened from this host.
*
* This describes the connection, not the client: a reverse proxy running on
* the same host also connects over loopback, so a true result does not mean
* the client is local. Use it only to decide whether the connection itself
* needs encrypting, never to grant access.
*/
function isLoopbackPeer(req) {
// The whole of 127.0.0.0/8 is loopback, not just 127.0.0.1. The address is
// absent on a socket that has already been destroyed.
const address = req.socket.remoteAddress;
return typeof address === 'string' && (
address.startsWith('127.') ||
address.startsWith('::ffff:127.') ||
address === '::1'
);
}

/**
* Checks whether a request originated on this host.
*
* Stricter than `isLoopbackPeer`, because a same-host reverse proxy would
* otherwise make every request it relays look local. A request counts as
* local only when nothing indicates it was relayed.
*/
function isLocalRequest(req) {
return req.connection.remoteAddress === '127.0.0.1' ||
req.connection.remoteAddress === '::ffff:127.0.0.1' ||
req.connection.remoteAddress === '::1';
// A configured proxy relays every request, so no request is local.
if (config.trustProxy) {
return false;
}
// Forwarding headers are never sent by a client on this host.
const isForwarded = Object.keys(req.headers).some(header =>
header.startsWith(forwardingHeaderPrefix) || forwardingHeaders.includes(header)
);
if (isForwarded) {
return false;
}
return isLoopbackPeer(req);
}

/**
Expand All @@ -102,11 +141,15 @@ module.exports = function(config, options) {
* - Requires users to be configured for remote access (unless dev mode is enabled)
*/
function enforceRemoteAccessRestrictions(req, res, next) {
if (!options.dev && !isLocalRequest(req)) {
if (!req.secure && !options.allowInsecureHTTP) {
if (!options.dev) {
// Keyed on the connection: a loopback connection cannot be observed off
// this host, so it needs no encryption of its own.
if (!isLoopbackPeer(req) && !req.secure && !options.allowInsecureHTTP) {
return res.status(403).json({ error: 'Parse Dashboard can only be remotely accessed via HTTPS' });
}
if (!users) {
// Keyed on the client: granting access requires knowing where the
// request came from, which a relayed request cannot establish.
if (!isLocalRequest(req) && !users) {
return res.status(401).json({ error: 'Configure a user to access Parse Dashboard remotely' });
}
}
Expand Down Expand Up @@ -135,13 +178,13 @@ module.exports = function(config, options) {
agent: config.agent,
};

if (!options.dev && !isLocalRequest(req)) {
if (!req.secure && !options.allowInsecureHTTP) {
if (!options.dev) {
if (!isLoopbackPeer(req) && !req.secure && !options.allowInsecureHTTP) {
//Disallow HTTP requests except on localhost, to prevent the master key from being transmitted in cleartext
return res.send({ success: false, error: 'Parse Dashboard can only be remotely accessed via HTTPS' });
}

if (!users) {
if (!isLocalRequest(req) && !users) {
//Accessing the dashboard over the internet can only be done with username and password
return res.send({ success: false, error: 'Configure a user to access Parse Dashboard remotely' });
}
Expand Down
11 changes: 9 additions & 2 deletions Parse-Dashboard/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,18 @@ module.exports = (options) => {
// Mount parseDashboard with authentication
app.use(mountPath, parseDashboard(config.data, dashboardOptions));

// 0.0.0.0 (IPv4) and :: (IPv6) are bind-all addresses: they are not directly
// navigable and browsers do not treat them as a secure context, which breaks
// Web APIs the dashboard relies on (e.g. crypto.randomUUID used by the Parse
// SDK). Advertise localhost instead so the printed URL actually works.
const displayHost = address =>
address === '0.0.0.0' || address === '::' ? 'localhost' : address;

let server;
if(!configSSLKey || !configSSLCert){
// Start the server.
server = app.listen(port, host, function () {
console.log(`The dashboard is now available at http://${server.address().address}:${server.address().port}${mountPath}`);
console.log(`The dashboard is now available at http://${displayHost(server.address().address)}:${server.address().port}${mountPath}`);

// Initialize browser control WebSocket if enabled
if (browserControlSetup) {
Expand All @@ -211,7 +218,7 @@ module.exports = (options) => {
key: privateKey,
cert: certificate
}, app).listen(port, host, function () {
console.log(`The dashboard is now available at https://${server.address().address}:${server.address().port}${mountPath}`);
console.log(`The dashboard is now available at https://${displayHost(server.address().address)}:${server.address().port}${mountPath}`);

// Initialize browser control WebSocket if enabled
if (browserControlSetup) {
Expand Down
42 changes: 42 additions & 0 deletions changelogs/CHANGELOG_alpha.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
# [9.3.0-alpha.5](https://github.com/parse-community/parse-dashboard/compare/9.3.0-alpha.4...9.3.0-alpha.5) (2026-08-11)


### Bug Fixes

* Remote access restrictions not enforced when running behind a reverse proxy ([#3436](https://github.com/parse-community/parse-dashboard/issues/3436)) ([60cb379](https://github.com/parse-community/parse-dashboard/commit/60cb3791bd95325ee04d5e354c034d54817c89ac))

# [9.3.0-alpha.4](https://github.com/parse-community/parse-dashboard/compare/9.3.0-alpha.3...9.3.0-alpha.4) (2026-07-25)


### Bug Fixes

* Info panel data not loaded when opened over an existing selection ([#3384](https://github.com/parse-community/parse-dashboard/issues/3384)) ([4879959](https://github.com/parse-community/parse-dashboard/commit/4879959f08286cb2e55515027f8b40e90ab392c1))

# [9.3.0-alpha.3](https://github.com/parse-community/parse-dashboard/compare/9.3.0-alpha.2...9.3.0-alpha.3) (2026-07-25)


### Bug Fixes

* Add Cancel and Add buttons to the add-row modal ([#1995](https://github.com/parse-community/parse-dashboard/issues/1995)) ([#3383](https://github.com/parse-community/parse-dashboard/issues/3383)) ([2d1b739](https://github.com/parse-community/parse-dashboard/commit/2d1b739dad3b39a5c42dfa56afa96b2b76ba26b2))

# [9.3.0-alpha.2](https://github.com/parse-community/parse-dashboard/compare/9.3.0-alpha.1...9.3.0-alpha.2) (2026-07-23)


### Bug Fixes

* Prevent Config table crash when a parameter value is null ([#3424](https://github.com/parse-community/parse-dashboard/issues/3424)) ([7150acf](https://github.com/parse-community/parse-dashboard/commit/7150acfb43a7ca60345e746d4cb9f4d38cb540aa))

# [9.3.0-alpha.1](https://github.com/parse-community/parse-dashboard/compare/9.2.1-alpha.1...9.3.0-alpha.1) (2026-07-18)


### Features

* React 19 + react-router 7 + GraphiQL 5 coordinated upgrade ([#3409](https://github.com/parse-community/parse-dashboard/issues/3409)) ([c3e16c2](https://github.com/parse-community/parse-dashboard/commit/c3e16c27d5c98e1981d1d7b06b33d3d9e355b183))

## [9.2.1-alpha.1](https://github.com/parse-community/parse-dashboard/compare/9.2.0...9.2.1-alpha.1) (2026-07-17)


### Bug Fixes

* Advertise localhost instead of 0.0.0.0 in the dashboard startup message ([#3410](https://github.com/parse-community/parse-dashboard/issues/3410)) ([33ce7b8](https://github.com/parse-community/parse-dashboard/commit/33ce7b8d283855f8398f6111d8f557081d11d018))

# [9.2.0-alpha.1](https://github.com/parse-community/parse-dashboard/compare/9.1.2-alpha.1...9.2.0-alpha.1) (2026-07-04)


Expand Down
Loading
Loading