openvibers/openvibe.chat Commits

The core slur list catches every inflection of the n-word family (an -erin' form passed a channel's nudge and the global feed); real words that share its first letters still pass (#199)

OP OpenVibers committed 18 days ago · cfe3c7800b · 2 files, +18 −1

Changes

ddiff --git a/server/chat/moderation-utils.js b/server/chat/moderation-utils.js
iindex 6a901ec..23e78ff 100644
---- a/server/chat/moderation-utils.js
++++ b/server/chat/moderation-utils.js
@@@ -42,7 +42,10 @@ const CORE_SLUR_CATEGORIES = [
label: 'N-word and variants',
patterns: [
// Base + plural forms: ****a, ****as, ****az, ****er, ****ers, ****az
-- '\\bn+i+g+g+(?:a+[sz]?|e+r+[sz]?)\\b',
++ // ...and every inflection of the -er form (-ers, -ering, -erin, -ered, -erish) and -ah/-uh: a line that used one
++ // passed both a channel's nudge and the global feed (2026-10-25). Real words that share the first letters
++ // (niggle, niggard) are not matched: neither continues with a or er.
++ '\\bn+i+g+g+(?:a+[hsz]?|u+h+|e+r+(?:[sz]|i+n+g*[sz]?|e+d+|i+s+h+)?)\\b',
// Alternate root family: nick, knick, etc.
'\\b[kn]*n+h?i+c?k+e+r+s?\\b',
],
ddiff --git a/test/global-standard.test.js b/test/global-standard.test.js
iindex 62ca758..66530c3 100644
---- a/test/global-standard.test.js
++++ b/test/global-standard.test.js
@@@ -53,4 +53,18 @@ assert.deepStrictEqual(guard.check({ id: 201, message: 'y', user_id: 5, mirrored
assert.strictEqual(guard.check({ id: 202, message: 'a whole sentence', user_id: 5, mirrored: true }).ok, true);
assert.strictEqual(guard.check({ id: 203, message: '#'.repeat(300), user_id: 5, mirrored: true }).ok, false, 'a mirrored wall');
++// Every inflection of the n-word family is a core slur, in any room and in the global feed (2026-10-25: an '-erin'
++// form passed both). Spelled out at run time from its parts; real words that share the first letters are not.
++{
++ const { containsCoreSlur } = require('../server/chat/moderation-utils');
++ const stem = ['n', 'i', 'g', 'g'].join('');
++ for (const tail of ['er', 'ers', 'a', 'as', 'az', 'ah', 'uh', 'ering', 'erin', 'erings', 'ered', 'erish']) {
++ assert.strictEqual(containsCoreSlur(`who is ${stem}${tail} your eggs`), true, `${stem.slice(0, 1)}…${tail}`);
++ assert.strictEqual(containsCoreSlur(`who is ${stem}${tail}\u2019 your eggs`), true, `${stem.slice(0, 1)}…${tail} with a curly apostrophe`);
++ assert.strictEqual(lineOk(`who is ${stem}${tail} your eggs`, { mirrored: true }), false);
++ }
++ for (const word of ['niggle', 'niggling', 'niggles', 'niggard', 'niggardly', 'snigger', 'sniggering'])
++ assert.strictEqual(containsCoreSlur(`a small ${word} about the stream`), false, word);
++}
++
console.log('global-standard: all checks passed');