@@ -3,6 +3,7 @@ package feedback
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
@@ -26,8 +27,25 @@ import (
|
||||
|
||||
const PackageMagic = "YMHUTFB1"
|
||||
|
||||
const (
|
||||
ErrorTooLarge = "TOO_LARGE"
|
||||
ErrorMissingField = "MISSING_FIELD"
|
||||
ErrorInvalidPayload = "INVALID_PAYLOAD"
|
||||
ErrorInvalidTimestamp = "INVALID_TIMESTAMP"
|
||||
ErrorInvalidSignature = "INVALID_SIGNATURE"
|
||||
ErrorInvalidPackage = "INVALID_PACKAGE"
|
||||
ErrorInvalidEncryptedPackage = "INVALID_ENCRYPTED_PACKAGE"
|
||||
ErrorDecryptFailed = "DECRYPT_FAILED"
|
||||
ErrorHashMismatch = "HASH_MISMATCH"
|
||||
ErrorServerConfig = "SERVER_CONFIG"
|
||||
)
|
||||
|
||||
var feedbackCodePattern = regexp.MustCompile(`^FB-[0-9]{8}-[A-F0-9]{6}$`)
|
||||
|
||||
type requestContextKey string
|
||||
|
||||
const duplicateContextKey requestContextKey = "ymhut.feedback.duplicate"
|
||||
|
||||
type Service struct {
|
||||
cfg *config.Config
|
||||
store *db.Store
|
||||
@@ -64,7 +82,7 @@ func (s *Service) Submit(r *http.Request) (db.Feedback, error) {
|
||||
if strings.Contains(contentType, "multipart/form-data") {
|
||||
if item, err := s.submitMultipart(r); err == nil {
|
||||
return item, nil
|
||||
} else if hasSignedFields(r) {
|
||||
} else if hasSignedFields(r) || !strings.Contains(strings.ToLower(err.Error()), "signed multipart fields are required") {
|
||||
return db.Feedback{}, err
|
||||
}
|
||||
}
|
||||
@@ -151,6 +169,7 @@ func (s *Service) submitMultipart(r *http.Request) (db.Feedback, error) {
|
||||
code = db.NewFeedbackCode()
|
||||
}
|
||||
if existing, err := s.store.GetFeedback(code); err == nil {
|
||||
setDuplicateSubmission(r, true)
|
||||
return existing, nil
|
||||
}
|
||||
file, _, err := r.FormFile("package")
|
||||
@@ -197,9 +216,48 @@ func (s *Service) submitMultipart(r *http.Request) (db.Feedback, error) {
|
||||
return db.Feedback{}, err
|
||||
}
|
||||
item := buildRecord(code, payload, info, encryptedPath, packagePath, packageSha256, strings.ToLower(payload.PlainPackageSha256), r.RemoteAddr)
|
||||
setDuplicateSubmission(r, false)
|
||||
return item, s.store.InsertFeedback(item)
|
||||
}
|
||||
|
||||
func DuplicateSubmission(r *http.Request) bool {
|
||||
duplicate, _ := r.Context().Value(duplicateContextKey).(bool)
|
||||
return duplicate
|
||||
}
|
||||
|
||||
func setDuplicateSubmission(r *http.Request, duplicate bool) {
|
||||
*r = *r.WithContext(context.WithValue(r.Context(), duplicateContextKey, duplicate))
|
||||
}
|
||||
|
||||
func LegacyError(err error) (string, int) {
|
||||
if err == nil {
|
||||
return "", http.StatusOK
|
||||
}
|
||||
lower := strings.ToLower(err.Error())
|
||||
switch {
|
||||
case strings.Contains(lower, "too large"):
|
||||
return ErrorTooLarge, http.StatusRequestEntityTooLarge
|
||||
case strings.Contains(lower, "signed multipart fields") || strings.Contains(lower, "missing package"):
|
||||
return ErrorMissingField, http.StatusBadRequest
|
||||
case strings.Contains(lower, "timestamp outside"):
|
||||
return ErrorInvalidTimestamp, http.StatusBadRequest
|
||||
case strings.Contains(lower, "invalid request signature"):
|
||||
return ErrorInvalidSignature, http.StatusUnauthorized
|
||||
case strings.Contains(lower, "hash mismatch") || strings.Contains(lower, "invalid package hash"):
|
||||
return ErrorHashMismatch, http.StatusBadRequest
|
||||
case strings.Contains(lower, "encrypted package format") || strings.Contains(lower, "encrypted package is required"):
|
||||
return ErrorInvalidEncryptedPackage, http.StatusBadRequest
|
||||
case strings.Contains(lower, "message authentication failed") || strings.Contains(lower, "decrypt"):
|
||||
return ErrorDecryptFailed, http.StatusBadRequest
|
||||
case strings.Contains(lower, "payload") || strings.Contains(lower, "json"):
|
||||
return ErrorInvalidPayload, http.StatusBadRequest
|
||||
case strings.Contains(lower, "zip") || strings.Contains(lower, "package"):
|
||||
return ErrorInvalidPackage, http.StatusBadRequest
|
||||
default:
|
||||
return ErrorServerConfig, http.StatusBadRequest
|
||||
}
|
||||
}
|
||||
|
||||
func hasSignedFields(r *http.Request) bool {
|
||||
if r.MultipartForm == nil {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user