mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 08:06:13 +00:00
fix: file "Keep" filter rules into the inbox so allow-lists beat the spam filter #1027
A bare `keep;` delivers to the default place, which on Stalwart is Junk for a message its spam filter has already classified, so an allow-list rule made with "Keep in inbox" changed nothing. The webmail now writes `fileinto "INBOX";` for it (62465e1e), but saving filters on the phone wrote `keep;` again and undid that. The action stays `keep` in the metadata, so scripts written by either client read back as "Keep in inbox" and save unchanged.
This commit is contained in:
@@ -209,9 +209,14 @@ describe('generateScript', () => {
|
||||
expect(script).toContain('reject "Go away";');
|
||||
});
|
||||
|
||||
it('generates keep', () => {
|
||||
it('generates keep as an explicit fileinto "INBOX" (#1027)', () => {
|
||||
// A bare `keep;` lets Stalwart's spam verdict decide the folder, so a
|
||||
// "Keep in inbox" allow-list rule must file into INBOX explicitly.
|
||||
const script = generateScript([makeRule({ actions: [{ type: 'keep' }] })]);
|
||||
expect(script).toContain('keep;');
|
||||
expect(script).toContain('fileinto "INBOX";');
|
||||
expect(script).not.toMatch(/^\s*keep;/m);
|
||||
const requireLine = script.split('\n').find(l => l.startsWith('require'))!;
|
||||
expect(requireLine).toContain('"fileinto"');
|
||||
});
|
||||
|
||||
it('generates stop', () => {
|
||||
@@ -434,9 +439,9 @@ describe('generateScript', () => {
|
||||
expect(requireLine).toContain('"imap4flags"');
|
||||
});
|
||||
|
||||
it('generates no require line when only keep/discard/stop/forward actions', () => {
|
||||
it('generates no require line when only discard/stop/forward actions', () => {
|
||||
const script = generateScript([makeRule({
|
||||
actions: [{ type: 'keep' }, { type: 'forward', value: 'a@b.com' }],
|
||||
actions: [{ type: 'forward', value: 'a@b.com' }, { type: 'stop' }],
|
||||
})]);
|
||||
expect(script).not.toContain('require');
|
||||
});
|
||||
|
||||
@@ -148,6 +148,38 @@ describe('parseScript', () => {
|
||||
const result = parseScript(script);
|
||||
expect(result.rules).toEqual(rules);
|
||||
});
|
||||
|
||||
it('reads a webmail "Keep in inbox" rule back as keep and saves it unchanged (#1027)', () => {
|
||||
// What the webmail writes since 62465e1e: the action stays `keep` in the
|
||||
// metadata, but the Sieve block files into INBOX explicitly.
|
||||
const rule = makeRule({
|
||||
id: 'allow',
|
||||
name: 'Allow boss',
|
||||
conditions: [{ field: 'from', comparator: 'is', value: 'boss@example.com' }],
|
||||
actions: [{ type: 'keep' }],
|
||||
stopProcessing: true,
|
||||
});
|
||||
const webScript = [
|
||||
'/* @metadata:begin',
|
||||
JSON.stringify({ version: 1, rules: [rule] }),
|
||||
'@metadata:end */',
|
||||
'',
|
||||
'require ["fileinto"];',
|
||||
'',
|
||||
'# Rule: Allow boss',
|
||||
'if header :is "From" "boss@example.com" {',
|
||||
' fileinto "INBOX";',
|
||||
' stop;',
|
||||
'}',
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
const result = parseScript(webScript);
|
||||
expect(result.isOpaque).toBe(false);
|
||||
expect(result.rules).toEqual([rule]);
|
||||
expect(generateScript(result.rules, result.vacation, { externalRequires: result.externalRequires }))
|
||||
.toBe(webScript);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validation edge cases', () => {
|
||||
|
||||
@@ -129,7 +129,11 @@ function generateActions(actions: FilterAction[]): string[] {
|
||||
case 'reject':
|
||||
return `reject "${escapeString(action.value || '')}";`;
|
||||
case 'keep':
|
||||
return 'keep;';
|
||||
// A bare `keep;` delivers to the "default place", which on Stalwart is
|
||||
// Junk for a message its spam filter has already classified, so an
|
||||
// allow-list rule made with "Keep" would change nothing. An explicit
|
||||
// `fileinto "INBOX"` is honored over the spam verdict (#1027).
|
||||
return 'fileinto "INBOX";';
|
||||
case 'stop':
|
||||
return 'stop;';
|
||||
}
|
||||
@@ -152,6 +156,7 @@ function computeRequires(rules: FilterRule[], vacation?: VacationSieveConfig): s
|
||||
for (const action of rule.actions) {
|
||||
switch (action.type) {
|
||||
case 'move':
|
||||
case 'keep':
|
||||
extensions.add('fileinto');
|
||||
break;
|
||||
case 'copy':
|
||||
|
||||
Reference in New Issue
Block a user