From 22be1498dd27a161f5de8daee4fe9408b06f1698 Mon Sep 17 00:00:00 2001 From: Yehor Komarov Date: Mon, 18 Nov 2024 14:09:51 +0300 Subject: [PATCH] Set time variables in transaction --- internal/corazawaf/rule_multiphase.go | 18 ++++++ internal/corazawaf/transaction.go | 77 ++++++++++++++++++++++++++ internal/corazawaf/transaction_test.go | 30 ++++++++++ internal/corazawaf/waf.go | 1 + internal/variables/variables.go | 18 +++--- 5 files changed, 135 insertions(+), 9 deletions(-) diff --git a/internal/corazawaf/rule_multiphase.go b/internal/corazawaf/rule_multiphase.go index 00033a837..099e23e6a 100644 --- a/internal/corazawaf/rule_multiphase.go +++ b/internal/corazawaf/rule_multiphase.go @@ -47,6 +47,24 @@ func minPhase(v variables.RuleVariable) types.RulePhase { return types.PhaseResponseHeaders case variables.UniqueID: return types.PhaseRequestHeaders + case variables.Time: + return types.PhaseRequestHeaders + case variables.TimeDay: + return types.PhaseRequestHeaders + case variables.TimeEpoch: + return types.PhaseRequestHeaders + case variables.TimeHour: + return types.PhaseRequestHeaders + case variables.TimeMin: + return types.PhaseRequestHeaders + case variables.TimeMon: + return types.PhaseRequestHeaders + case variables.TimeSec: + return types.PhaseRequestHeaders + case variables.TimeWday: + return types.PhaseRequestHeaders + case variables.TimeYear: + return types.PhaseRequestHeaders case variables.ArgsCombinedSize: // Size changes between phase 1 and 2 so evaluate both times return types.PhaseRequestHeaders diff --git a/internal/corazawaf/transaction.go b/internal/corazawaf/transaction.go index 2fe3585c8..753e6a47e 100644 --- a/internal/corazawaf/transaction.go +++ b/internal/corazawaf/transaction.go @@ -282,6 +282,24 @@ func (tx *Transaction) Collection(idx variables.RuleVariable) collection.Collect return tx.variables.multipartPartHeaders case variables.MultipartStrictError: return tx.variables.multipartStrictError + case variables.Time: + return tx.variables.time + case variables.TimeDay: + return tx.variables.timeDay + case variables.TimeEpoch: + return tx.variables.timeEpoch + case variables.TimeHour: + return tx.variables.timeHour + case variables.TimeMin: + return tx.variables.timeMin + case variables.TimeMon: + return tx.variables.timeMon + case variables.TimeSec: + return tx.variables.timeSec + case variables.TimeWday: + return tx.variables.timeWday + case variables.TimeYear: + return tx.variables.timeYear } return collections.Noop @@ -1591,6 +1609,20 @@ func (tx *Transaction) generateResponseBodyError(err error) { tx.variables.resBodyProcessorErrorMsg.Set(err.Error()) } +// setTimeVariables sets all the time variables +func (tx *Transaction) setTimeVariables() { + timestamp := time.Unix(0, tx.Timestamp) + tx.variables.time.Set(timestamp.Format(time.TimeOnly)) + tx.variables.timeDay.Set(strconv.Itoa(timestamp.Day())) + tx.variables.timeEpoch.Set(strconv.FormatInt(timestamp.Unix(), 10)) + tx.variables.timeHour.Set(strconv.Itoa(timestamp.Hour())) + tx.variables.timeMin.Set(strconv.Itoa(timestamp.Minute())) + tx.variables.timeSec.Set(strconv.Itoa(timestamp.Second())) + tx.variables.timeWday.Set(strconv.Itoa(int(timestamp.Weekday()))) + tx.variables.timeMon.Set(strconv.Itoa(int(timestamp.Month()))) + tx.variables.timeYear.Set(strconv.Itoa(timestamp.Year())) +} + // TransactionVariables has pointers to all the variables of the transaction type TransactionVariables struct { args *collections.ConcatKeyed @@ -1669,6 +1701,15 @@ type TransactionVariables struct { resBodyErrorMsg *collections.Single resBodyProcessorError *collections.Single resBodyProcessorErrorMsg *collections.Single + time *collections.Single + timeDay *collections.Single + timeEpoch *collections.Single + timeHour *collections.Single + timeMin *collections.Single + timeMon *collections.Single + timeSec *collections.Single + timeWday *collections.Single + timeYear *collections.Single } func NewTransactionVariables() *TransactionVariables { @@ -1741,6 +1782,15 @@ func NewTransactionVariables() *TransactionVariables { v.requestXML = collections.NewMap(variables.RequestXML) v.multipartPartHeaders = collections.NewMap(variables.MultipartPartHeaders) v.multipartStrictError = collections.NewSingle(variables.MultipartStrictError) + v.time = collections.NewSingle(variables.Time) + v.timeDay = collections.NewSingle(variables.TimeDay) + v.timeEpoch = collections.NewSingle(variables.TimeEpoch) + v.timeHour = collections.NewSingle(variables.TimeHour) + v.timeMin = collections.NewSingle(variables.TimeMin) + v.timeMon = collections.NewSingle(variables.TimeMon) + v.timeSec = collections.NewSingle(variables.TimeSec) + v.timeWday = collections.NewSingle(variables.TimeWday) + v.timeYear = collections.NewSingle(variables.TimeYear) // XML is a pointer to RequestXML v.xml = v.requestXML @@ -2299,6 +2349,33 @@ func (v *TransactionVariables) All(f func(v variables.RuleVariable, col collecti if !f(variables.XML, v.xml) { return } + if !f(variables.Time, v.time) { + return + } + if !f(variables.TimeDay, v.timeDay) { + return + } + if !f(variables.TimeEpoch, v.timeEpoch) { + return + } + if !f(variables.TimeHour, v.timeHour) { + return + } + if !f(variables.TimeMin, v.timeMin) { + return + } + if !f(variables.TimeMon, v.timeMon) { + return + } + if !f(variables.TimeSec, v.timeSec) { + return + } + if !f(variables.TimeWday, v.timeWday) { + return + } + if !f(variables.TimeYear, v.timeYear) { + return + } } type formattable interface { diff --git a/internal/corazawaf/transaction_test.go b/internal/corazawaf/transaction_test.go index 07881c092..de986cbfa 100644 --- a/internal/corazawaf/transaction_test.go +++ b/internal/corazawaf/transaction_test.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/corazawaf/coraza/v3/collection" "github.com/corazawaf/coraza/v3/debuglog" @@ -67,6 +68,23 @@ func TestTxSetters(t *testing.T) { validateMacroExpansion(exp, tx, t) } + +func TestTxTime(t *testing.T) { + tx := makeTransactionTimestamped(t) + exp := map[string]string{ + "%{TIME}": "15:27:34", + "%{TIME_DAY}": "18", + "%{TIME_EPOCH}": fmt.Sprintf("%d", tx.Timestamp/1e9), // 1731943654 in UTC, may differ in local timezone + "%{TIME_HOUR}": "15", + "%{TIME_MIN}": "27", + "%{TIME_MON}": "11", + "%{TIME_SEC}": "34", + "%{TIME_WDAY}": "1", + "%{TIME_YEAR}": "2024", + } + validateMacroExpansion(exp, tx, t) +} + func TestTxMultipart(t *testing.T) { tx := NewWAF().NewTransaction() body := []string{ @@ -1360,6 +1378,18 @@ func makeTransaction(t testing.TB) *Transaction { return tx } +func makeTransactionTimestamped(t testing.TB) *Transaction { + t.Helper() + tx := NewWAF().NewTransaction() + timestamp, err := time.ParseInLocation(time.DateTime, "2024-11-18 15:27:34", time.Local) + if err != nil { + panic(err) + } + tx.Timestamp = timestamp.UnixNano() + tx.setTimeVariables() + return tx +} + func makeTransactionMultipart(t *testing.T) *Transaction { if t != nil { t.Helper() diff --git a/internal/corazawaf/waf.go b/internal/corazawaf/waf.go index e4430a815..55db79942 100644 --- a/internal/corazawaf/waf.go +++ b/internal/corazawaf/waf.go @@ -242,6 +242,7 @@ func (w *WAF) newTransaction(opts Options) *Transaction { tx.variables.duration.Set("0") tx.variables.highestSeverity.Set("0") tx.variables.uniqueID.Set(tx.id) + tx.setTimeVariables() tx.debugLogger.Debug().Msg("Transaction started") diff --git a/internal/variables/variables.go b/internal/variables/variables.go index 14ecd7c6b..9a792994a 100644 --- a/internal/variables/variables.go +++ b/internal/variables/variables.go @@ -236,22 +236,22 @@ const ( ResBodyProcessorError // ResBodyProcessorErrorMsg ResBodyProcessorErrorMsg - // Time + // Time holds a formatted string representing the time (hour:minute:second). Time - // TimeDay + // TimeDay holds the current day of the month (1-31) TimeDay - // TimeEpoch + // TimeEpoch holds the time in seconds since 1970 TimeEpoch - // TimeHour + // TimeHour holds the current hour of the day (0-23) TimeHour - // TimeMin + // TimeMin holds the current minute of the hour (0-59) TimeMin - // TimeMon + // TimeMon holds the current month of the year (0-11) TimeMon - // TimeSec + // TimeSec holds the current second of the minute (0-59) TimeSec - // TimeWday + // TimeWday holds the current weekday value (1–7), where Monday is 1 TimeWday - // TimeYear + // TimeYear the current four-digit year value TimeYear )