From c93180b9e1bf0eedf64aacf666abae26dfa5225b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Cegie=C5=82ka?= Date: Thu, 12 Dec 2024 13:22:20 +0100 Subject: [PATCH 1/2] fix: PC-15140 Add at least second time resolution for firstEventStart field (#608) ## Motivation We want `budgetAdjustments` `firstStartEvent` to have a minimum of seconds defined (without nanoseconds). Saving with nanoseconds could cause problems when using the budget adjustments API. --------- Co-authored-by: kubaceg --- .../v1alpha/budgetadjustment/validation.go | 11 ++- .../budgetadjustment/validation_test.go | 76 ++++++++++++++----- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/manifest/v1alpha/budgetadjustment/validation.go b/manifest/v1alpha/budgetadjustment/validation.go index 26117cf6..53710d44 100644 --- a/manifest/v1alpha/budgetadjustment/validation.go +++ b/manifest/v1alpha/budgetadjustment/validation.go @@ -41,7 +41,8 @@ var specValidation = govy.New[Spec]( Rules(validationV1Alpha.StringDescription()), govy.For(func(s Spec) time.Time { return s.FirstEventStart }). WithName("firstEventStart"). - Required(), + Required(). + Rules(secondTimePrecision), govy.Transform(func(s Spec) string { return s.Duration }, time.ParseDuration). WithName("duration"). Required(). @@ -98,3 +99,11 @@ var atLeastHourlyFreq = govy.NewRule(func(rule *rrule.RRule) error { return nil }) + +var secondTimePrecision = govy.NewRule(func(t time.Time) error { + if t.Nanosecond() != 0 { + return errors.New("time must be defined with 1s precision") + } + + return nil +}) diff --git a/manifest/v1alpha/budgetadjustment/validation_test.go b/manifest/v1alpha/budgetadjustment/validation_test.go index af78c40c..4bc54e44 100644 --- a/manifest/v1alpha/budgetadjustment/validation_test.go +++ b/manifest/v1alpha/budgetadjustment/validation_test.go @@ -74,7 +74,7 @@ func TestValidate_Spec(t *testing.T) { name: "description too long", spec: Spec{ Description: strings.Repeat("A", 2000), - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Filters: Filters{SLOs: []SLORef{{Name: "my-slo", Project: "default"}}}, }, @@ -101,7 +101,7 @@ func TestValidate_Spec(t *testing.T) { { name: "duration required", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Filters: Filters{SLOs: []SLORef{{Name: "my-slo", Project: "default"}}}, }, expectedErrors: []testutils.ExpectedError{ @@ -114,7 +114,7 @@ func TestValidate_Spec(t *testing.T) { { name: "no slo filters", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Filters: Filters{}, }, @@ -128,7 +128,7 @@ func TestValidate_Spec(t *testing.T) { { name: "too short duration", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1s", Filters: Filters{ SLOs: []SLORef{{ @@ -147,7 +147,7 @@ func TestValidate_Spec(t *testing.T) { { name: "duration contains seconds", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m1s", Filters: Filters{ SLOs: []SLORef{{ @@ -166,7 +166,7 @@ func TestValidate_Spec(t *testing.T) { { name: "slo is defined without name", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Filters: Filters{ SLOs: []SLORef{{ @@ -184,7 +184,7 @@ func TestValidate_Spec(t *testing.T) { { name: "slo is defined with invalid slo name", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Filters: Filters{ SLOs: []SLORef{{ @@ -203,7 +203,7 @@ func TestValidate_Spec(t *testing.T) { { name: "slo is defined without project", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Filters: Filters{ SLOs: []SLORef{{ @@ -221,7 +221,7 @@ func TestValidate_Spec(t *testing.T) { { name: "slo is defined with invalid project name", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Filters: Filters{ SLOs: []SLORef{{ @@ -240,7 +240,7 @@ func TestValidate_Spec(t *testing.T) { { name: "wrong rrule format", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "some test", Filters: Filters{ @@ -260,7 +260,7 @@ func TestValidate_Spec(t *testing.T) { { name: "invalid rrule", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "FREQ=TEST;INTERVAL=2", Filters: Filters{ @@ -280,7 +280,7 @@ func TestValidate_Spec(t *testing.T) { { name: "invalid freq in rrule", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "FREQ=MINUTELY;INTERVAL=2;COUNT=10", Filters: Filters{ @@ -300,7 +300,7 @@ func TestValidate_Spec(t *testing.T) { { name: "rrule with dtstart trows transform error", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "DTSTART:20240909T065900Z\\nRRULE:FREQ=MINUTELY;BYHOUR=6,8,9,10,11,12,13,14,15,16;COUNT=10", Filters: Filters{ @@ -320,7 +320,7 @@ func TestValidate_Spec(t *testing.T) { { name: "proper freq in rrule", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "FREQ=HOURLY;INTERVAL=1", Filters: Filters{ @@ -335,7 +335,7 @@ func TestValidate_Spec(t *testing.T) { { name: "duplicate slo", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "FREQ=WEEKLY;INTERVAL=2", Filters: Filters{ @@ -355,7 +355,7 @@ func TestValidate_Spec(t *testing.T) { { name: "duplicate slo with multiple others", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "FREQ=WEEKLY;INTERVAL=2", Filters: Filters{ @@ -380,7 +380,7 @@ func TestValidate_Spec(t *testing.T) { { name: "proper spec", spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Rrule: "FREQ=WEEKLY;INTERVAL=2", Filters: Filters{ @@ -418,7 +418,7 @@ func validBudgetAdjustment() BudgetAdjustment { DisplayName: "My Budget Adjustment", }, Spec: Spec{ - FirstEventStart: time.Now(), + FirstEventStart: time.Now().Truncate(time.Second), Duration: "1m", Filters: Filters{ SLOs: []SLORef{ @@ -542,3 +542,43 @@ func TestAtLeastHourlyFreq(t *testing.T) { }) } } + +func TestAtLeastSecondTimeResolution(t *testing.T) { + tests := []struct { + name string + time time.Time + expectedError string + }{ + { + name: "time with nanosecond returns error", + time: time.Date(2023, time.January, 1, 0, 0, 0, 1, time.UTC), + expectedError: "time must be defined with 1s precision", + }, + { + name: "time with second resolution returns no error", + time: time.Date(2023, time.January, 1, 0, 0, 0, 0, time.UTC), + expectedError: "", + }, + { + name: "time with millisecond resolution returns error", + time: time.Date(2023, time.January, 1, 0, 0, 0, 1000000, time.UTC), + expectedError: "time must be defined with 1s precision", + }, + { + name: "time with microsecond resolution returns error", + time: time.Date(2023, time.January, 1, 0, 0, 0, 526, time.UTC), + expectedError: "time must be defined with 1s precision", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := secondTimePrecision.Validate(tt.time) + if tt.expectedError == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.expectedError) + } + }) + } +} From 4afa716656a59661e369e07532033a8fe97c7951 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:26:27 +0000 Subject: [PATCH 2/2] chore: Update module golang.org/x/crypto to v0.31.0 [SECURITY] (#610) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | golang.org/x/crypto | `v0.23.0` -> `v0.31.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fcrypto/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/golang.org%2fx%2fcrypto/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/golang.org%2fx%2fcrypto/v0.23.0/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fcrypto/v0.23.0/v0.31.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | ### GitHub Vulnerability Alerts #### [CVE-2024-45337](https://redirect.github.com/golang/crypto/commit/b4f1988a35dee11ec3e05d6bf3e90b695fbd8909) Applications and libraries which misuse the ServerConfig.PublicKeyCallback callback may be susceptible to an authorization bypass. The documentation for ServerConfig.PublicKeyCallback says that "A call to this function does not guarantee that the key offered is in fact used to authenticate." Specifically, the SSH protocol allows clients to inquire about whether a public key is acceptable before proving control of the corresponding private key. PublicKeyCallback may be called with multiple keys, and the order in which the keys were provided cannot be used to infer which key the client successfully authenticated with, if any. Some applications, which store the key(s) passed to PublicKeyCallback (or derived information) and make security relevant determinations based on it once the connection is established, may make incorrect assumptions. For example, an attacker may send public keys A and B, and then authenticate with A. PublicKeyCallback would be called only twice, first with A and then with B. A vulnerable application may then make authorization decisions based on key B for which the attacker does not actually control the private key. Since this API is widely misused, as a partial mitigation golang.org/x/cry...@​v0.31.0 enforces the property that, when successfully authenticating via public key, the last key passed to ServerConfig.PublicKeyCallback will be the key used to authenticate the connection. PublicKeyCallback will now be called multiple times with the same key, if necessary. Note that the client may still not control the last key passed to PublicKeyCallback if the connection is then authenticated with a different method, such as PasswordCallback, KeyboardInteractiveCallback, or NoClientAuth. Users should be using the Extensions field of the Permissions return value from the various authentication callbacks to record data associated with the authentication attempt instead of referencing external state. Once the connection is established the state corresponding to the successful authentication attempt can be retrieved via the ServerConn.Permissions field. Note that some third-party libraries misuse the Permissions type by sharing it across authentication attempts; users of third-party libraries should refer to the relevant projects for guidance. --- ### Configuration 📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. â™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/nobl9/nobl9-go). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/mock_example/go.mod | 8 ++++---- docs/mock_example/go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/mock_example/go.mod b/docs/mock_example/go.mod index 0d6e39bf..c7492a11 100644 --- a/docs/mock_example/go.mod +++ b/docs/mock_example/go.mod @@ -27,12 +27,12 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/teambition/rrule-go v1.8.2 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.24.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect diff --git a/docs/mock_example/go.sum b/docs/mock_example/go.sum index 25bdcfa3..a56f4c12 100644 --- a/docs/mock_example/go.sum +++ b/docs/mock_example/go.sum @@ -57,20 +57,20 @@ github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRV github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=