2021-11-19 11:44:48 -05:00
import { strict as assert } from "assert" ;
import config from "../../src/config" ;
import { newTestUser } from "./clientHelper" ;
import { MatrixClient } from "matrix-bot-sdk" ;
import BanList , { ChangeType , ListRuleChange , RULE_USER } from "../../src/models/BanList" ;
/ * *
* Create a policy rule in a policy room .
* @param client A matrix client that is logged in
* @param policyRoomId The room id to add the policy to .
* @param policyType The type of policy to add e . g . m . policy . rule . user . ( Use RULE_USER though ) .
* @param entity The entity to ban e . g . @foo : example . org
* @param reason A reason for the rule e . g . 'Wouldn' t stop posting spam links '
* @returns The event id of the newly created policy rule .
* /
async function createPolicyRule ( client : MatrixClient , policyRoomId : string , policyType : string , entity : string , reason : string ) {
return await client . sendStateEvent ( policyRoomId , policyType , ` rule: ${ entity } ` , {
entity ,
reason ,
recommendation : 'm.ban'
} ) ;
}
describe ( "Test: Updating the BanList" , function ( ) {
it ( "Calculates what has changed correctly." , async function ( ) {
this . timeout ( 10000 ) ;
const mjolnir = config . RUNTIME . client !
2022-01-25 07:19:44 -05:00
const moderator = await newTestUser ( { name : { contains : "moderator" } } ) ;
2021-11-19 11:44:48 -05:00
const banListId = await mjolnir . createRoom ( { invite : [ await moderator . getUserId ( ) ] } ) ;
const banList = new BanList ( banListId , banListId , mjolnir ) ;
mjolnir . setUserPowerLevel ( await moderator . getUserId ( ) , banListId , 100 ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( banList . allRules . length , 0 ) ;
2021-11-19 11:44:48 -05:00
// Test adding a new rule
await createPolicyRule ( mjolnir , banListId , RULE_USER , '@added:localhost:9999' , '' ) ;
let changes : ListRuleChange [ ] = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 , 'There should only be one change' ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Added ) ;
assert . equal ( changes [ 0 ] . sender , await mjolnir . getUserId ( ) ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( banList . userRules . length , 1 ) ;
assert . equal ( banList . allRules . length , 1 ) ;
2021-11-19 11:44:48 -05:00
// Test modifiying a rule
let originalEventId = await createPolicyRule ( mjolnir , banListId , RULE_USER , '@modified:localhost:9999' , '' ) ;
await banList . updateList ( ) ;
2021-11-22 10:41:12 -05:00
let modifyingEventId = await createPolicyRule ( mjolnir , banListId , RULE_USER , '@modified:localhost:9999' , 'modified reason' ) ;
2021-11-19 11:44:48 -05:00
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Modified ) ;
assert . equal ( changes [ 0 ] . previousState [ 'event_id' ] , originalEventId , 'There should be a previous state event for a modified rule' ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , modifyingEventId ) ;
let modifyingAgainEventId = await createPolicyRule ( mjolnir , banListId , RULE_USER , '@modified:localhost:9999' , 'modified again' ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Modified ) ;
assert . equal ( changes [ 0 ] . previousState [ 'event_id' ] , modifyingEventId , 'There should be a previous state event for a modified rule' ) ;
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , modifyingAgainEventId ) ;
assert . equal ( banList . userRules . length , 2 , 'There should be two rules, one for @modified:localhost:9999 and one for @added:localhost:9999' ) ;
2021-11-19 11:44:48 -05:00
// Test redacting a rule
const redactThis = await createPolicyRule ( mjolnir , banListId , RULE_USER , '@redacted:localhost:9999' , '' ) ;
await banList . updateList ( ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( banList . userRules . filter ( r = > r . entity === '@redacted:localhost:9999' ) . length , 1 ) ;
2021-11-19 11:44:48 -05:00
await mjolnir . redactEvent ( banListId , redactThis ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Removed ) ;
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , redactThis , 'Should show the new version of the event with redacted content' ) ;
assert . equal ( Object . keys ( changes [ 0 ] . event [ 'content' ] ) . length , 0 , 'Should show the new version of the event with redacted content' ) ;
assert . notEqual ( Object . keys ( changes [ 0 ] . previousState [ 'content' ] ) , 0 , 'Should have a copy of the unredacted state' ) ;
assert . notEqual ( changes [ 0 ] . rule , undefined , 'The previous rule should be present' ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( banList . userRules . filter ( r = > r . entity === '@redacted:localhost:9999' ) . length , 0 , 'The rule should be removed.' ) ;
2021-11-19 11:44:48 -05:00
// Test soft redaction of a rule
const softRedactedEntity = '@softredacted:localhost:9999'
await createPolicyRule ( mjolnir , banListId , RULE_USER , softRedactedEntity , '' ) ;
await banList . updateList ( ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( banList . userRules . filter ( r = > r . entity === softRedactedEntity ) . length , 1 ) ;
await mjolnir . sendStateEvent ( banListId , RULE_USER , ` rule: ${ softRedactedEntity } ` , { } ) ;
2021-11-19 11:44:48 -05:00
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Removed ) ;
assert . equal ( Object . keys ( changes [ 0 ] . event [ 'content' ] ) . length , 0 , 'Should show the new version of the event with redacted content' ) ;
assert . notEqual ( Object . keys ( changes [ 0 ] . previousState [ 'content' ] ) , 0 , 'Should have a copy of the unredacted state' ) ;
assert . notEqual ( changes [ 0 ] . rule , undefined , 'The previous rule should be present' ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( banList . userRules . filter ( r = > r . entity === softRedactedEntity ) . length , 0 , 'The rule should have been removed' ) ;
2021-11-19 11:44:48 -05:00
// Now test a double soft redaction just to make sure stuff doesn't explode
2021-11-22 10:41:12 -05:00
await mjolnir . sendStateEvent ( banListId , RULE_USER , ` rule: ${ softRedactedEntity } ` , { } ) ;
2021-11-19 11:44:48 -05:00
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 0 , "It shouldn't detect a double soft redaction as a change, it should be seen as adding an invalid rule." ) ;
2021-11-22 10:41:12 -05:00
assert . equal ( banList . userRules . filter ( r = > r . entity === softRedactedEntity ) . length , 0 , 'The rule should have been removed' ) ;
// Test that different (old) rule types will be modelled as the latest event type.
originalEventId = await createPolicyRule ( mjolnir , banListId , 'org.matrix.mjolnir.rule.user' , '@old:localhost:9999' , '' ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Added ) ;
assert . equal ( banList . userRules . filter ( r = > r . entity === '@old:localhost:9999' ) . length , 1 ) ;
modifyingEventId = await createPolicyRule ( mjolnir , banListId , 'm.room.rule.user' , '@old:localhost:9999' , 'modified reason' ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Modified ) ;
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , modifyingEventId ) ;
assert . equal ( changes [ 0 ] . previousState [ 'event_id' ] , originalEventId , 'There should be a previous state event for a modified rule' ) ;
assert . equal ( banList . userRules . filter ( r = > r . entity === '@old:localhost:9999' ) . length , 1 ) ;
modifyingAgainEventId = await createPolicyRule ( mjolnir , banListId , RULE_USER , '@old:localhost:9999' , 'changes again' ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Modified ) ;
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , modifyingAgainEventId ) ;
assert . equal ( changes [ 0 ] . previousState [ 'event_id' ] , modifyingEventId , 'There should be a previous state event for a modified rule' ) ;
assert . equal ( banList . userRules . filter ( r = > r . entity === '@old:localhost:9999' ) . length , 1 ) ;
} )
it ( "Will remove rules with old types when they are 'soft redacted' with a different but more recent event type." , async function ( ) {
this . timeout ( 3000 ) ;
const mjolnir = config . RUNTIME . client !
2022-01-25 07:19:44 -05:00
const moderator = await newTestUser ( { name : { contains : "moderator" } } ) ;
2021-11-22 10:41:12 -05:00
const banListId = await mjolnir . createRoom ( { invite : [ await moderator . getUserId ( ) ] } ) ;
const banList = new BanList ( banListId , banListId , mjolnir ) ;
mjolnir . setUserPowerLevel ( await moderator . getUserId ( ) , banListId , 100 ) ;
const entity = '@old:localhost:9999' ;
let originalEventId = await createPolicyRule ( mjolnir , banListId , 'm.room.rule.user' , entity , '' ) ;
let changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Added ) ;
assert . equal ( banList . userRules . filter ( rule = > rule . entity === entity ) . length , 1 , 'There should be a rule stored that we just added...' )
let softRedactingEventId = await mjolnir . sendStateEvent ( banListId , RULE_USER , ` rule: ${ entity } ` , { } ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Removed ) ;
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , softRedactingEventId ) ;
assert . equal ( changes [ 0 ] . previousState [ 'event_id' ] , originalEventId , 'There should be a previous state event for a modified rule' ) ;
assert . equal ( banList . userRules . filter ( rule = > rule . entity === entity ) . length , 0 , 'The rule should no longer be stored.' ) ;
} )
it ( "A rule of the most recent type won't be deleted when an old rule is deleted for the same entity." , async function ( ) {
this . timeout ( 3000 ) ;
const mjolnir = config . RUNTIME . client !
2022-01-25 07:19:44 -05:00
const moderator = await newTestUser ( { name : { contains : "moderator" } } ) ;
2021-11-22 10:41:12 -05:00
const banListId = await mjolnir . createRoom ( { invite : [ await moderator . getUserId ( ) ] } ) ;
const banList = new BanList ( banListId , banListId , mjolnir ) ;
mjolnir . setUserPowerLevel ( await moderator . getUserId ( ) , banListId , 100 ) ;
const entity = '@old:localhost:9999' ;
let originalEventId = await createPolicyRule ( mjolnir , banListId , 'm.room.rule.user' , entity , '' ) ;
let changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Added ) ;
assert . equal ( banList . userRules . filter ( rule = > rule . entity === entity ) . length , 1 , 'There should be a rule stored that we just added...' )
let updatedEventId = await createPolicyRule ( mjolnir , banListId , RULE_USER , entity , '' ) ;
changes = await banList . updateList ( ) ;
// If in the future you change this and it fails, it's really subjective whether this constitutes a modification, since the only thing that has changed
// is the rule type. The actual content is identical.
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Modified ) ;
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , updatedEventId ) ;
assert . equal ( changes [ 0 ] . previousState [ 'event_id' ] , originalEventId , 'There should be a previous state event for a modified rule' ) ;
assert . equal ( banList . userRules . filter ( rule = > rule . entity === entity ) . length , 1 , 'Only the latest version of the rule gets returned.' ) ;
// Now we delete the old version of the rule without consequence.
await mjolnir . sendStateEvent ( banListId , 'm.room.rule.user' , ` rule: ${ entity } ` , { } ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 0 ) ;
assert . equal ( banList . userRules . filter ( rule = > rule . entity === entity ) . length , 1 , 'The rule should still be active.' ) ;
// And we can still delete the new version of the rule.
let softRedactingEventId = await mjolnir . sendStateEvent ( banListId , RULE_USER , ` rule: ${ entity } ` , { } ) ;
changes = await banList . updateList ( ) ;
assert . equal ( changes . length , 1 ) ;
assert . equal ( changes [ 0 ] . changeType , ChangeType . Removed ) ;
assert . equal ( changes [ 0 ] . event [ 'event_id' ] , softRedactingEventId ) ;
assert . equal ( changes [ 0 ] . previousState [ 'event_id' ] , updatedEventId , 'There should be a previous state event for a modified rule' ) ;
assert . equal ( banList . userRules . filter ( rule = > rule . entity === entity ) . length , 0 , 'The rule should no longer be stored.' ) ;
2021-11-19 11:44:48 -05:00
} )
} ) ;