mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-19 17:44:44 +00:00
* docs: PR2 GDPR IP/privacy audit design spec Second of five GDPR PRs (#6701). Audit identifies four log-sites that leak IPs despite disableIPlogging=true, proposes a tri-state ipLogging setting with a back-compat shim, and specifies a doc/privacy.md that documents Etherpad's actual IP handling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: PR2 GDPR IP/privacy audit implementation plan 7 TDD-structured tasks: anonymizeIp helper + unit tests, tri-state ipLogging setting with disableIPlogging deprecation shim, wiring through 5 leaking log sites, clientVars.clientIp removal, access-log integration test, doc/privacy.md, and PR handoff. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(gdpr): anonymizeIp helper with v4/v6/v4-mapped truncation * feat(gdpr): tri-state ipLogging setting + disableIPlogging shim * fix(gdpr): route every IP log site through anonymizeIp Closes four leaks where disableIPlogging was silently ignored (rate-limit warn, both auth-log calls in webaccess, import/export rate-limit warn) and normalises the four that did honour the flag onto the new ipLogging tri-state via the shared helper. * chore(gdpr): drop dead clientVars.clientIp placeholder Server side: remove the literal '127.0.0.1' assignments from both clientVars and collab_client_vars. Type side: drop clientIp from ClientVarPayload and ServerVar. pad.getClientIp now returns the same '127.0.0.1' literal as a plugin-compat shim (pad_utils.uniqueId still uses it as a prefix). * test(gdpr): ipLogging modes + disableIPlogging shim * docs(gdpr): operator-facing privacy and IP handling statement * fix(gdpr): validate ipLogging at load + regression test for log sites Qodo review: - settings.ipLogging is loaded as a trusted union but nothing enforced the shape. An unknown value (e.g. a typo or null) silently fell through to anonymizeIp's "truncated" branch and emitted partially redacted IPs. Fall back to "anonymous" with a WARN at load time. - New regression test scans the four known log-sites for raw req.ip / socket.request.ip / request.ip inside logger calls that don't wrap through anonymizeIp / logIp, so a future edit that re-introduces a raw IP fails CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e39dbde887
commit
6195289198
15 changed files with 1308 additions and 17 deletions
|
|
@ -34,6 +34,8 @@ import settings, {
|
|||
exportAvailable,
|
||||
sofficeAvailable
|
||||
} from '../utils/Settings';
|
||||
import {anonymizeIp} from '../utils/anonymizeIp';
|
||||
const logIp = (ip: string | null | undefined) => anonymizeIp(ip, settings.ipLogging);
|
||||
const securityManager = require('../db/SecurityManager');
|
||||
const plugins = require('../../static/js/pluginfw/plugin_defs');
|
||||
import log4js from 'log4js';
|
||||
|
|
@ -231,7 +233,7 @@ exports.handleDisconnect = async (socket:any) => {
|
|||
accessLogger.info('[LEAVE]' +
|
||||
` pad:${session.padId}` +
|
||||
` socket:${socket.id}` +
|
||||
` IP:${settings.disableIPlogging ? 'ANONYMOUS' : socket.request.ip}` +
|
||||
` IP:${logIp(socket.request.ip)}` +
|
||||
` authorID:${session.author}` +
|
||||
(user && user.username ? ` username:${user.username}` : ''));
|
||||
/* eslint-enable prefer-template */
|
||||
|
|
@ -339,7 +341,7 @@ exports.handleMessage = async (socket:any, message: ClientVarMessage) => {
|
|||
try {
|
||||
await rateLimiter.consume(socket.request.ip); // consume 1 point per event from IP
|
||||
} catch (err) {
|
||||
messageLogger.warn(`Rate limited IP ${socket.request.ip}. To reduce the amount of rate ` +
|
||||
messageLogger.warn(`Rate limited IP ${logIp(socket.request.ip)}. To reduce the amount of rate ` +
|
||||
'limiting that happens edit the rateLimit values in settings.json');
|
||||
stats.meter('rateLimited').mark();
|
||||
socket.emit('message', {disconnect: 'rateLimited'});
|
||||
|
|
@ -384,7 +386,7 @@ exports.handleMessage = async (socket:any, message: ClientVarMessage) => {
|
|||
|
||||
const auth = thisSession.auth;
|
||||
if (!auth) {
|
||||
const ip = settings.disableIPlogging ? 'ANONYMOUS' : (socket.request.ip || '<unknown>');
|
||||
const ip = logIp(socket.request.ip);
|
||||
const msg = JSON.stringify(message, null, 2);
|
||||
throw new Error(`pre-CLIENT_READY message from IP ${ip}: ${msg}`);
|
||||
}
|
||||
|
|
@ -401,7 +403,7 @@ exports.handleMessage = async (socket:any, message: ClientVarMessage) => {
|
|||
throw new Error([
|
||||
'Author ID changed mid-session. Bad or missing token or sessionID?',
|
||||
`socket:${socket.id}`,
|
||||
`IP:${settings.disableIPlogging ? 'ANONYMOUS' : socket.request.ip}`,
|
||||
`IP:${logIp(socket.request.ip)}`,
|
||||
`originalAuthorID:${thisSession.author}`,
|
||||
`newAuthorID:${authorID}`,
|
||||
...(user && user.username) ? [`username:${user.username}`] : [],
|
||||
|
|
@ -1006,7 +1008,7 @@ const handleClientReady = async (socket:any, message: ClientReadyMessage) => {
|
|||
accessLogger.info(`[${pad.head > 0 ? 'ENTER' : 'CREATE'}]` +
|
||||
` pad:${sessionInfo.padId}` +
|
||||
` socket:${socket.id}` +
|
||||
` IP:${settings.disableIPlogging ? 'ANONYMOUS' : socket.request.ip}` +
|
||||
` IP:${logIp(socket.request.ip)}` +
|
||||
` authorID:${sessionInfo.author}` +
|
||||
(user && user.username ? ` username:${user.username}` : ''));
|
||||
/* eslint-enable prefer-template */
|
||||
|
|
@ -1116,7 +1118,6 @@ const handleClientReady = async (socket:any, message: ClientReadyMessage) => {
|
|||
savedRevisions: pad.getSavedRevisions(),
|
||||
collab_client_vars: {
|
||||
initialAttributedText: atext,
|
||||
clientIp: '127.0.0.1',
|
||||
padId: sessionInfo.auth.padID,
|
||||
historicalAuthorData,
|
||||
apool,
|
||||
|
|
@ -1124,7 +1125,6 @@ const handleClientReady = async (socket:any, message: ClientReadyMessage) => {
|
|||
time: currentTime,
|
||||
},
|
||||
colorPalette: authorManager.getColorPalette(),
|
||||
clientIp: '127.0.0.1',
|
||||
userColor: authorColorId,
|
||||
padId: sessionInfo.auth.padID,
|
||||
padOptions: settings.padOptions,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue