From 58aa979caed484c51b2cd86b6eb47ad142ac0844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20I=C3=B1iguez=20Goia?= Date: Mon, 10 Jun 2024 23:54:54 +0200 Subject: [PATCH] fixed loading rules when Created field is a timestamp When exporting rules from the GUI, the Created field was exported as timestamp. Importing rules worked fine, because json.Marshall() accepts the timestamp format. However, when the daemon was loading a rule with the Created field as timestamp, since the field was defined as time.Time, it expected a RFC3339 string (https://pkg.go.dev/time#Time.UnmarshalJSON) so it failed to parse the timestamp and the rule was not loaded. Now the field is defined as string, it's always saved as RFC3339, and if we fail to parse these fields we'll use a temporary date instead of failing loading the rule. More info: https://github.com/evilsocket/opensnitch/issues/1140#issuecomment-2140904847 Closes #1140 --- daemon/rule/loader.go | 3 ++- daemon/rule/rule.go | 33 +++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/daemon/rule/loader.go b/daemon/rule/loader.go index 6f37abe5..e10188ab 100644 --- a/daemon/rule/loader.go +++ b/daemon/rule/loader.go @@ -182,7 +182,8 @@ func (l *Loader) Replace(rule *Rule, saveToDisk bool) error { // Save a rule to disk. func (l *Loader) Save(rule *Rule, path string) error { - rule.Updated = time.Now() + // When saving the rule, use always RFC3339 format for the Created field (#1140). + rule.Updated = time.Now().Format(time.RFC3339) raw, err := json.MarshalIndent(rule, "", " ") if err != nil { return fmt.Errorf("Error while saving rule %s to %s: %s", rule, path, err) diff --git a/daemon/rule/rule.go b/daemon/rule/rule.go index 8e5101b7..46d33629 100644 --- a/daemon/rule/rule.go +++ b/daemon/rule/rule.go @@ -38,22 +38,24 @@ const ( // The fields match the ones saved as json to disk. // If a .json rule file is modified on disk, it's reloaded automatically. type Rule struct { - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - Name string `json:"name"` - Description string `json:"description"` - Action Action `json:"action"` - Duration Duration `json:"duration"` - Operator Operator `json:"operator"` - Enabled bool `json:"enabled"` - Precedence bool `json:"precedence"` - Nolog bool `json:"nolog"` + // Save date fields as string, to avoid issues marshalling Time (#1140). + Created string `json:"created"` + Updated string `json:"updated"` + + Name string `json:"name"` + Description string `json:"description"` + Action Action `json:"action"` + Duration Duration `json:"duration"` + Operator Operator `json:"operator"` + Enabled bool `json:"enabled"` + Precedence bool `json:"precedence"` + Nolog bool `json:"nolog"` } // Create creates a new rule object with the specified parameters. func Create(name, description string, enabled, precedence, nolog bool, action Action, duration Duration, op *Operator) *Rule { return &Rule{ - Created: time.Now(), + Created: time.Now().Format(time.RFC3339), Enabled: enabled, Precedence: precedence, Nolog: nolog, @@ -135,8 +137,15 @@ func (r *Rule) Serialize() *protocol.Rule { r.Operator.Lock() defer r.Operator.Unlock() + created, err := time.Parse(time.RFC3339, r.Created) + if err != nil { + log.Warning("Error parsing rule Created date (it should be in RFC3339 format): %s (%s)", err, string(r.Name)) + log.Warning("using current time instead: %s", created) + created = time.Now() + } + protoRule := &protocol.Rule{ - Created: r.Created.Unix(), + Created: created.Unix(), Name: string(r.Name), Description: string(r.Description), Enabled: bool(r.Enabled),