Merge pull request 'Merge upstream v0.16.24' (#84) from merge/upstream-v0.16.24 into main
This commit was merged in pull request #84.
This commit is contained in:
@@ -570,8 +570,8 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> {
|
||||
|
||||
// inbuxa: rules are always to hand, since a copy ships with the server
|
||||
// (spam_rules). They load on first boot, and again when the bundled
|
||||
// version differs from the one last loaded, which only adds what's
|
||||
// missing: new tags and rules, never a changed score.
|
||||
// rules differ from the ones last loaded: new tags and rules, fixes to
|
||||
// rules nobody edited, never a changed score or an admin's edit.
|
||||
let rules_url = super::spam_rules::rules_url(
|
||||
bp.registry
|
||||
.object::<SpamSettings>(Id::singleton())
|
||||
@@ -582,7 +582,7 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> {
|
||||
&& super::spam_rules::applied_version(&bp.data_store)
|
||||
.await?
|
||||
.as_deref()
|
||||
!= Some(super::spam_rules::BUNDLED_SPAM_RULES_VERSION);
|
||||
!= Some(super::spam_rules::BUNDLED_SPAM_RULES_APPLIED);
|
||||
if bp.registry.count_object(ObjectType::SpamRule).await? == 0 || bundled_is_new {
|
||||
let mut batch = BatchBuilder::new();
|
||||
batch.schedule_task(Task::SpamFilterMaintenance(TaskSpamFilterMaintenance {
|
||||
|
||||
@@ -12,11 +12,16 @@
|
||||
//! and license) and uses it whenever no other source is configured. The rules
|
||||
//! URL remains an operator override (`https://` or `file://`).
|
||||
//!
|
||||
//! Loading rules only ever adds what's missing, never changes an existing rule
|
||||
//! or score. They load on first boot, and again whenever the bundled version
|
||||
//! differs from the one last applied, so an upgrade brings new tags (the AI
|
||||
//! classifier's `LLM_*` scores, say) to an install that already had rules.
|
||||
//! Loading rules adds what's missing and brings an existing rule up to date,
|
||||
//! but never touches one an admin edited: every object an update writes is
|
||||
//! fingerprinted, and one that no longer matches its fingerprint is kept as
|
||||
//! it is. Tags (scores) are never replaced. Switching a rule on or off isn't
|
||||
//! an edit, and is kept either way. They load on first boot, and again
|
||||
//! whenever the bundled rules differ from the ones last applied, so an
|
||||
//! upgrade brings new tags (the AI classifier's `LLM_*` scores, say) and
|
||||
//! fixed rules to an install that already had rules.
|
||||
|
||||
use registry::{schema::prelude::ObjectType, types::EnumImpl};
|
||||
use std::io::Read;
|
||||
use store::{
|
||||
SUBSPACE_INBUXA, Store, ValueKey,
|
||||
@@ -27,13 +32,17 @@ use trc::AddContext;
|
||||
/// The version of spam-filter the embedded rules come from.
|
||||
pub const BUNDLED_SPAM_RULES_VERSION: &str = "3.0.2";
|
||||
|
||||
/// What's recorded once the bundled rules are loaded: their version, then the
|
||||
/// fork's own generation of the update, so a change to how an update applies
|
||||
/// runs it once more. Generation 2 fingerprints (upstream v0.16.24).
|
||||
pub const BUNDLED_SPAM_RULES_APPLIED: &str = "3.0.2+2";
|
||||
|
||||
static BUNDLED_SPAM_RULES: &[u8] =
|
||||
include_bytes!("../../../../resources/spam-filter/spam-filter-rules.json.gz");
|
||||
|
||||
/// Upstream's default rules source, the value every install created before
|
||||
/// the rules were bundled has saved. Read only to treat it as unset.
|
||||
const LEGACY_DEFAULT_URL: &str =
|
||||
"https://github.com/stalwartlabs/spam-filter/releases/latest/download/spam-filter-rules.json.gz";
|
||||
const LEGACY_DEFAULT_URL: &str = "https://github.com/stalwartlabs/spam-filter/releases/latest/download/spam-filter-rules.json.gz";
|
||||
|
||||
/// The URL to fetch rules from, or `None` for the bundled rules. An empty
|
||||
/// setting and upstream's old default both mean the bundled rules.
|
||||
@@ -57,14 +66,49 @@ fn applied_key() -> ValueClass {
|
||||
})
|
||||
}
|
||||
|
||||
/// The bundled version last loaded into the registry, if any.
|
||||
fn fingerprint_key(object: ObjectType, id: u64) -> ValueClass {
|
||||
let mut key = b"Sf".to_vec();
|
||||
key.extend_from_slice(object.as_str().as_bytes());
|
||||
key.push(0);
|
||||
key.extend_from_slice(&id.to_be_bytes());
|
||||
ValueClass::Any(AnyClass {
|
||||
subspace: SUBSPACE_INBUXA,
|
||||
key,
|
||||
})
|
||||
}
|
||||
|
||||
/// The fingerprint of what a rules update last wrote to this object, if one
|
||||
/// did.
|
||||
pub async fn fingerprint(data: &Store, object: ObjectType, id: u64) -> trc::Result<Option<String>> {
|
||||
data.get_value::<String>(ValueKey::from(fingerprint_key(object, id)))
|
||||
.await
|
||||
.caused_by(trc::location!())
|
||||
}
|
||||
|
||||
/// Records the fingerprint of what a rules update wrote to this object.
|
||||
pub async fn set_fingerprint(
|
||||
data: &Store,
|
||||
object: ObjectType,
|
||||
id: u64,
|
||||
fingerprint: &str,
|
||||
) -> trc::Result<()> {
|
||||
let mut batch = BatchBuilder::new();
|
||||
batch.set(fingerprint_key(object, id), fingerprint.as_bytes().to_vec());
|
||||
data.write(batch.build_all())
|
||||
.await
|
||||
.caused_by(trc::location!())
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// The bundled rules last loaded into the registry, if any
|
||||
/// ([`BUNDLED_SPAM_RULES_APPLIED`]'s form).
|
||||
pub async fn applied_version(data: &Store) -> trc::Result<Option<String>> {
|
||||
data.get_value::<String>(ValueKey::from(applied_key()))
|
||||
.await
|
||||
.caused_by(trc::location!())
|
||||
}
|
||||
|
||||
/// Records that the bundled rules of this version have been loaded.
|
||||
/// Records that the bundled rules have been loaded.
|
||||
pub async fn set_applied_version(data: &Store, version: &str) -> trc::Result<()> {
|
||||
let mut batch = BatchBuilder::new();
|
||||
batch.set(applied_key(), version.as_bytes().to_vec());
|
||||
@@ -90,6 +134,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn applied_marker_names_the_bundled_version() {
|
||||
assert!(
|
||||
BUNDLED_SPAM_RULES_APPLIED
|
||||
.strip_prefix(BUNDLED_SPAM_RULES_VERSION)
|
||||
.is_some_and(|generation| generation.starts_with('+'))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bundled_rules_parse_and_score_the_ai_tags() {
|
||||
let rules: serde_json::Value = serde_json::from_slice(&bundled_rules().unwrap()).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user