@@ -5,13 +5,13 @@ import (
5
5
"encoding/base64"
6
6
"encoding/json"
7
7
"fmt"
8
- "io/ioutil "
8
+ "io"
9
9
"log"
10
10
"net"
11
11
"net/http"
12
12
"os"
13
13
"path"
14
- "path/filepath "
14
+ "regexp "
15
15
"strconv"
16
16
"strings"
17
17
"time"
@@ -24,6 +24,7 @@ import (
24
24
"github.com/MakeNowJust/heredoc/v2"
25
25
"github.com/briandowns/spinner"
26
26
"github.com/gerald1248/httpscerts"
27
+ "github.com/go-playground/validator/v10"
27
28
"github.com/spf13/cobra"
28
29
"golang.org/x/net/websocket"
29
30
)
@@ -40,6 +41,9 @@ var SSLFlag bool
40
41
// LocalFlag can be set to false to emulate a remote environment
41
42
var LocalFlag bool
42
43
44
+ // Valditor for input validation
45
+ var validate * validator.Validate
46
+
43
47
func checkPortAvailability (port int ) bool {
44
48
address := fmt .Sprintf ("localhost:%d" , port )
45
49
conn , err := net .Dial ("tcp" , address )
@@ -195,16 +199,28 @@ func init() {
195
199
serveCmd .Flags ().StringVarP (& ConfigFileFlag , "config" , "c" , "plenti.json" , "use a custom sitewide configuration file" )
196
200
}
197
201
202
+ // Validate user supplied values
198
203
type localChange struct {
199
- Action string
200
- Encoding string
201
- File string
202
- Contents string
204
+ Action string `json:"action" validate:"required,oneof=create update delete"`
205
+ Encoding string `json:"encoding" validate:"required,oneof=base64 text"`
206
+ File string `json:"file" validate:"file-path"`
207
+ Contents string `json:"contents" validate:"required"`
208
+ }
209
+
210
+ // Custom validation for file path. Only allow files in the layouts and content directories.
211
+ func FilePathValidation (fl validator.FieldLevel ) bool {
212
+ reFilePath := regexp .MustCompile (`^(content)[a-zA-Z0-9_\-\/]*(.json)$` )
213
+ fmt .Println (fl .Field ().String ())
214
+ return reFilePath .MatchString (fl .Field ().String ())
203
215
}
204
216
205
217
func postLocal (w http.ResponseWriter , r * http.Request ) {
218
+ // Register custom rules to validator
219
+ validate = validator .New ()
220
+ validate .RegisterValidation ("file-path" , FilePathValidation )
221
+
206
222
if r .Method == "POST" {
207
- b , err := ioutil .ReadAll (r .Body )
223
+ b , err := io .ReadAll (r .Body )
208
224
if err != nil {
209
225
fmt .Printf ("Could not read 'body' from local edit: %v" , err )
210
226
}
@@ -213,10 +229,16 @@ func postLocal(w http.ResponseWriter, r *http.Request) {
213
229
if err != nil {
214
230
fmt .Printf ("Could not unmarshal JSON data: %v" , err )
215
231
}
232
+
216
233
var contents []byte
217
- currentDir , _ := os .Getwd ()
218
234
for _ , change := range localChanges {
219
- change .File = filepath .Join (currentDir , filepath .Clean ("/" + change .File ))
235
+
236
+ // Validate user input, there is any error, return 400 Bad Request
237
+ err := validate .Struct (change )
238
+ if err != nil {
239
+ http .Error (w , err .Error (), http .StatusBadRequest )
240
+ return
241
+ }
220
242
221
243
if change .Action == "create" || change .Action == "update" {
222
244
contents = []byte (change .Contents )
@@ -267,7 +289,7 @@ func serveSSL(port int) {
267
289
Handler : nil ,
268
290
ReadTimeout : 10 * time .Second ,
269
291
WriteTimeout : 10 * time .Second ,
270
- ErrorLog : log .New (ioutil .Discard , "" , 0 ),
292
+ ErrorLog : log .New (io .Discard , "" , 0 ),
271
293
MaxHeaderBytes : 1 << 20 ,
272
294
TLSConfig : cfg ,
273
295
}
0 commit comments