@@ -296,8 +296,9 @@ export default function AuditDetailsPage(props: Props) {
|
|||||||
onDrop={files => void handleUploadFile(files)}
|
onDrop={files => void handleUploadFile(files)}
|
||||||
accept={{
|
accept={{
|
||||||
"application/pdf": [".pdf"],
|
"application/pdf": [".pdf"],
|
||||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
"application/msword": [".doc"],
|
||||||
[".docx"],
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"],
|
||||||
|
"application/vnd.oasis.opendocument.text": [".odt"],
|
||||||
}}
|
}}
|
||||||
maxSize={25}
|
maxSize={25}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1597,7 +1597,7 @@ func TestAudit_UploadReport_Validation(t *testing.T) {
|
|||||||
|
|
||||||
frameworkID := factory.NewFramework(owner).WithName("Framework for Upload Validation").Create()
|
frameworkID := factory.NewFramework(owner).WithName("Framework for Upload Validation").Create()
|
||||||
|
|
||||||
t.Run("reject non-PDF file", func(t *testing.T) {
|
t.Run("reject non document file", func(t *testing.T) {
|
||||||
auditID := factory.NewAudit(owner, frameworkID).WithName("Invalid File Test").Create()
|
auditID := factory.NewAudit(owner, frameworkID).WithName("Invalid File Test").Create()
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
@@ -1611,7 +1611,7 @@ func TestAudit_UploadReport_Validation(t *testing.T) {
|
|||||||
`
|
`
|
||||||
|
|
||||||
// Try to upload a text file
|
// Try to upload a text file
|
||||||
textContent := []byte("This is not a PDF file")
|
textContent := []byte("This is not a document file")
|
||||||
|
|
||||||
err := owner.ExecuteWithFile(query, map[string]any{
|
err := owner.ExecuteWithFile(query, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
@@ -1619,14 +1619,14 @@ func TestAudit_UploadReport_Validation(t *testing.T) {
|
|||||||
"file": nil,
|
"file": nil,
|
||||||
},
|
},
|
||||||
}, "input.file", testutil.UploadFile{
|
}, "input.file", testutil.UploadFile{
|
||||||
Filename: "not-a-pdf.txt",
|
Filename: "not-a-document.txt",
|
||||||
ContentType: "text/plain",
|
ContentType: "text/plain",
|
||||||
Content: textContent,
|
Content: textContent,
|
||||||
}, nil)
|
}, nil)
|
||||||
require.Error(t, err, "Should reject non-PDF file")
|
require.Error(t, err, "Should reject non-document file")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("reject file with wrong extension but PDF content-type", func(t *testing.T) {
|
t.Run("reject file with wrong extension but document content-type", func(t *testing.T) {
|
||||||
auditID := factory.NewAudit(owner, frameworkID).WithName("Wrong Extension Test").Create()
|
auditID := factory.NewAudit(owner, frameworkID).WithName("Wrong Extension Test").Create()
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
@@ -1640,7 +1640,7 @@ func TestAudit_UploadReport_Validation(t *testing.T) {
|
|||||||
`
|
`
|
||||||
|
|
||||||
// Try to upload with wrong extension
|
// Try to upload with wrong extension
|
||||||
textContent := []byte("Not a real PDF")
|
textContent := []byte("Not a real document")
|
||||||
|
|
||||||
err := owner.ExecuteWithFile(query, map[string]any{
|
err := owner.ExecuteWithFile(query, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package filevalidation
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -162,6 +163,10 @@ func (v *FileValidator) WithMaxFileSize(maxSize int64) *FileValidator {
|
|||||||
|
|
||||||
// Validate validates that the file meets the configured requirements
|
// Validate validates that the file meets the configured requirements
|
||||||
func (v *FileValidator) Validate(filename string, contentType string, size int64) error {
|
func (v *FileValidator) Validate(filename string, contentType string, size int64) error {
|
||||||
|
if size == 0 {
|
||||||
|
return fmt.Errorf("file can't be empty")
|
||||||
|
}
|
||||||
|
|
||||||
if size > v.MaxFileSize {
|
if size > v.MaxFileSize {
|
||||||
return fmt.Errorf("file size exceeds maximum allowed size of %d bytes", v.MaxFileSize)
|
return fmt.Errorf("file size exceeds maximum allowed size of %d bytes", v.MaxFileSize)
|
||||||
}
|
}
|
||||||
@@ -180,13 +185,7 @@ func (v *FileValidator) Validate(filename string, contentType string, size int64
|
|||||||
return fmt.Errorf("file extension %q is not allowed", ext)
|
return fmt.Errorf("file extension %q is not allowed", ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
validType := false
|
validType := slices.Contains(allowedTypes, contentType)
|
||||||
for _, allowedType := range allowedTypes {
|
|
||||||
if contentType == allowedType {
|
|
||||||
validType = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !validType {
|
if !validType {
|
||||||
return fmt.Errorf("content type %q does not match extension %q", contentType, ext)
|
return fmt.Errorf("content type %q does not match extension %q", contentType, ext)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"go.gearno.de/crypto/uuid"
|
"go.gearno.de/crypto/uuid"
|
||||||
"go.gearno.de/kit/pg"
|
"go.gearno.de/kit/pg"
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
|
"go.probo.inc/probo/pkg/filevalidation"
|
||||||
"go.probo.inc/probo/pkg/gid"
|
"go.probo.inc/probo/pkg/gid"
|
||||||
"go.probo.inc/probo/pkg/page"
|
"go.probo.inc/probo/pkg/page"
|
||||||
"go.probo.inc/probo/pkg/validator"
|
"go.probo.inc/probo/pkg/validator"
|
||||||
@@ -88,8 +89,19 @@ func (uarr *UploadAuditReportRequest) Validate() error {
|
|||||||
v := validator.New()
|
v := validator.New()
|
||||||
|
|
||||||
v.Check(uarr.AuditID, "audit_id", validator.Required(), validator.GID(coredata.AuditEntityType))
|
v.Check(uarr.AuditID, "audit_id", validator.Required(), validator.GID(coredata.AuditEntityType))
|
||||||
|
if err := v.Error(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return v.Error()
|
fv := filevalidation.NewValidator(
|
||||||
|
filevalidation.WithCategories(filevalidation.CategoryDocument),
|
||||||
|
filevalidation.WithMaxFileSize(25*1024*1024),
|
||||||
|
)
|
||||||
|
if err := fv.Validate(uarr.File.Filename, uarr.File.ContentType, uarr.File.Size); err != nil {
|
||||||
|
return fmt.Errorf("invalid audit report file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s AuditService) Get(
|
func (s AuditService) Get(
|
||||||
@@ -313,6 +325,10 @@ func (s AuditService) UploadReport(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req UploadAuditReportRequest,
|
req UploadAuditReportRequest,
|
||||||
) (*coredata.Audit, error) {
|
) (*coredata.Audit, error) {
|
||||||
|
if err := req.Validate(); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
audit := &coredata.Audit{}
|
audit := &coredata.Audit{}
|
||||||
|
|
||||||
err := s.svc.pg.WithTx(
|
err := s.svc.pg.WithTx(
|
||||||
|
|||||||
Reference in New Issue
Block a user