@@ -2,10 +2,12 @@ package v4
2
2
3
3
import (
4
4
"encoding/json"
5
+ "fmt"
5
6
"strings"
6
7
"time"
7
8
8
9
"github.com/brave/go-update/extension"
10
+ "github.com/go-playground/validator/v10"
9
11
)
10
12
11
13
// GetElapsedDays calculates elapsed days since Jan 1, 2007
@@ -28,20 +30,21 @@ func GetUpdateStatus(extension extension.Extension) string {
28
30
// MarshalJSON encodes the extension list into response JSON
29
31
func (r * UpdateResponse ) MarshalJSON () ([]byte , error ) {
30
32
type URL struct {
31
- URL string `json:"url"`
33
+ URL string `json:"url" validate:"required" `
32
34
}
33
35
type Out struct {
34
- SHA256 string `json:"sha256"`
36
+ SHA256 string `json:"sha256" validate:"required" `
35
37
}
36
38
type In struct {
37
- SHA256 string `json:"sha256"`
39
+ SHA256 string `json:"sha256" validate:"required" `
38
40
}
39
41
type Operation struct {
40
- Type string `json:"type"`
41
- Out * Out `json:"out,omitempty"`
42
- In * In `json:"in,omitempty"`
43
- URLs []URL `json:"urls,omitempty"`
44
- Previous * In `json:"previous,omitempty"`
42
+ Type string `json:"type" validate:"required,oneof=download puff crx3"`
43
+ Out * Out `json:"out,omitempty" validate:"omitempty,required_if=Type download"`
44
+ In * In `json:"in,omitempty" validate:"omitempty,required_if=Type crx3"`
45
+ URLs []URL `json:"urls,omitempty" validate:"omitempty,required_if=Type download,dive"`
46
+ Previous * In `json:"previous,omitempty" validate:"omitempty,required_if=Type puff"`
47
+ Size uint64 `json:"size,omitempty" validate:"omitempty,required_if=Type download,gt=0"`
45
48
}
46
49
type Pipeline struct {
47
50
PipelineID string `json:"pipeline_id"`
@@ -79,7 +82,15 @@ func (r *UpdateResponse) MarshalJSON() ([]byte, error) {
79
82
},
80
83
}
81
84
85
+ // Create validator instance
86
+ validate := validator .New ()
87
+
82
88
for _ , ext := range * r {
89
+ // Check if SHA256 is empty
90
+ if ext .SHA256 == "" {
91
+ return nil , fmt .Errorf ("extension %s has empty SHA256" , ext .ID )
92
+ }
93
+
83
94
app := App {AppID : ext .ID , Status : "ok" }
84
95
updateStatus := GetUpdateStatus (ext )
85
96
app .UpdateCheck = UpdateCheck {Status : updateStatus }
@@ -97,6 +108,11 @@ func (r *UpdateResponse) MarshalJSON() ([]byte, error) {
97
108
// Add diff pipeline if patch is available (diff pipeline should come first)
98
109
if ext .FP != "" && ext .PatchList != nil {
99
110
if patchInfo , ok := ext .PatchList [ext .FP ]; ok {
111
+ // Check if hashdiff is empty
112
+ if patchInfo .Hashdiff == "" {
113
+ return nil , fmt .Errorf ("extension %s has empty Hashdiff" , ext .ID )
114
+ }
115
+
100
116
fpPrefix := ext .FP
101
117
if len (ext .FP ) >= 8 {
102
118
fpPrefix = ext .FP [:8 ]
@@ -105,22 +121,49 @@ func (r *UpdateResponse) MarshalJSON() ([]byte, error) {
105
121
patchURL := "https://" + extension .GetS3ExtensionBucketHost (ext .ID ) + "/release/" +
106
122
ext .ID + "/patches/" + ext .SHA256 + "/" + ext .FP + ".puff"
107
123
124
+ // Create the Out struct for diff pipeline
125
+ diffOut := & Out {
126
+ SHA256 : patchInfo .Hashdiff ,
127
+ }
128
+
129
+ // Create URLs for diff pipeline
130
+ diffURLs := []URL {{URL : patchURL }}
131
+
132
+ // Create In structs
133
+ previousIn := & In {SHA256 : ext .FP }
134
+ crx3In := & In {SHA256 : ext .SHA256 }
135
+
136
+ // Create operations for diff pipeline
137
+ diffDownloadOp := Operation {
138
+ Type : "download" ,
139
+ Out : diffOut ,
140
+ URLs : diffURLs ,
141
+ Size : normalizeSize (uint64 (patchInfo .Sizediff )),
142
+ }
143
+
144
+ puffOp := Operation {
145
+ Type : "puff" ,
146
+ Previous : previousIn ,
147
+ }
148
+
149
+ crx3Op := Operation {
150
+ Type : "crx3" ,
151
+ In : crx3In ,
152
+ }
153
+
154
+ // Validate all operations
155
+ for _ , op := range []Operation {diffDownloadOp , puffOp , crx3Op } {
156
+ if err := validate .Struct (op ); err != nil {
157
+ return nil , fmt .Errorf ("%s operation validation failed for extension %s: %v" , op .Type , ext .ID , err )
158
+ }
159
+ }
160
+
108
161
diffPipeline := Pipeline {
109
162
PipelineID : diffPipelineID ,
110
163
Operations : []Operation {
111
- {
112
- Type : "download" ,
113
- Out : & Out {SHA256 : patchInfo .Hashdiff },
114
- URLs : []URL {{URL : patchURL }},
115
- },
116
- {
117
- Type : "puff" ,
118
- Previous : & In {SHA256 : ext .FP },
119
- },
120
- {
121
- Type : "crx3" ,
122
- In : & In {SHA256 : ext .SHA256 },
123
- },
164
+ diffDownloadOp ,
165
+ puffOp ,
166
+ crx3Op ,
124
167
},
125
168
}
126
169
@@ -129,18 +172,38 @@ func (r *UpdateResponse) MarshalJSON() ([]byte, error) {
129
172
}
130
173
131
174
// Add full pipeline as fallback (always add as the last pipeline)
175
+ out := & Out {
176
+ SHA256 : ext .SHA256 ,
177
+ }
178
+
179
+ urls := []URL {{URL : url }}
180
+ mainCrx3In := & In {SHA256 : ext .SHA256 }
181
+
182
+ // Create operations for main pipeline
183
+ mainDownloadOp := Operation {
184
+ Type : "download" ,
185
+ Out : out ,
186
+ URLs : urls ,
187
+ Size : normalizeSize (ext .Size ),
188
+ }
189
+
190
+ mainCrx3Op := Operation {
191
+ Type : "crx3" ,
192
+ In : mainCrx3In ,
193
+ }
194
+
195
+ // Validate all operations in the main pipeline
196
+ for _ , op := range []Operation {mainDownloadOp , mainCrx3Op } {
197
+ if err := validate .Struct (op ); err != nil {
198
+ return nil , fmt .Errorf ("%s operation validation failed for extension %s: %v" , op .Type , ext .ID , err )
199
+ }
200
+ }
201
+
132
202
pipeline := Pipeline {
133
203
PipelineID : "direct_full" ,
134
204
Operations : []Operation {
135
- {
136
- Type : "download" ,
137
- Out : & Out {SHA256 : ext .SHA256 },
138
- URLs : []URL {{URL : url }},
139
- },
140
- {
141
- Type : "crx3" ,
142
- In : & In {SHA256 : ext .SHA256 },
143
- },
205
+ mainDownloadOp ,
206
+ mainCrx3Op ,
144
207
},
145
208
}
146
209
@@ -156,3 +219,10 @@ func (r *UpdateResponse) MarshalJSON() ([]byte, error) {
156
219
157
220
return json .Marshal (jsonResponse )
158
221
}
222
+
223
+ func normalizeSize (size uint64 ) uint64 {
224
+ if size == 0 {
225
+ return 1
226
+ }
227
+ return size
228
+ }
0 commit comments