Freeze SCUM SQLite template run contract
This commit is contained in:
@@ -16,10 +16,15 @@ const (
|
||||
maxSCUMProbeSamples = 3
|
||||
maxSCUMProbeTimeoutMS = 10000
|
||||
maxSCUMProbeResultBytes = 1024 * 1024
|
||||
maxSCUMTemplateParameters = 64
|
||||
maxSCUMTemplateRows = 1000
|
||||
maxSCUMTemplateBusyTimeoutMS = 1000
|
||||
maxSCUMTemplateValueBytes = 4096
|
||||
)
|
||||
|
||||
var scumHashPattern = regexp.MustCompile(`^sha256:[a-fA-F0-9]{64}$|^[a-fA-F0-9]{16,128}$`)
|
||||
var scumDataTargetSafeErrorPattern = regexp.MustCompile(`^data_target_[a-z0-9_]{1,80}$`)
|
||||
var scumTemplateKeyPattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_.-]{0,127}$`)
|
||||
|
||||
func ValidateSCUMSchemaProbeRequest(request domain.SCUMSchemaProbeRequest) error {
|
||||
var violations []string
|
||||
@@ -30,6 +35,37 @@ func ValidateSCUMSchemaProbeRequest(request domain.SCUMSchemaProbeRequest) error
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateSCUMSQLiteTemplateRequest(request domain.SCUMSQLiteTemplateRequest) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "requestId", request.RequestID)
|
||||
violations = appendRequired(violations, "jobId", request.JobID)
|
||||
violations = append(violations, validateSCUMBindingIdentity("binding", request.Binding)...)
|
||||
if !validSCUMReadCapability(request.Capability) {
|
||||
violations = append(violations, "capability must be a read capability")
|
||||
}
|
||||
violations = append(violations, validateSCUMTemplateKey("targetKey", request.TargetKey)...)
|
||||
violations = append(violations, validateSCUMTemplateKey("templateKey", request.TemplateKey)...)
|
||||
violations = appendRequired(violations, "adapterVersion", request.AdapterVersion)
|
||||
if request.AdapterVersion != "" && containsSCUMProtectedMaterial(request.AdapterVersion) {
|
||||
violations = append(violations, "adapterVersion contains protected material")
|
||||
}
|
||||
if request.AdapterVersion != "" && request.Binding.AdapterVersion != "" && request.AdapterVersion != request.Binding.AdapterVersion {
|
||||
violations = append(violations, "adapterVersion must match binding.adapterVersion")
|
||||
}
|
||||
if !validSCUMFingerprint(request.RequiredSchemaFingerprint) {
|
||||
violations = append(violations, "requiredSchemaFingerprint must be a digest/fingerprint")
|
||||
}
|
||||
if !validSCUMDigest(request.AssetDigest) {
|
||||
violations = append(violations, "assetDigest must be sha256 digest")
|
||||
}
|
||||
if !validSCUMDigest(request.ParameterDigest) {
|
||||
violations = append(violations, "parameterDigest must be sha256 digest")
|
||||
}
|
||||
violations = append(violations, validateSCUMSQLiteTemplateBounds("bounds", request.Bounds)...)
|
||||
violations = append(violations, validateSCUMValueMap("parameters", request.Parameters, request.Bounds.MaxParameters)...)
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateSCUMSchemaProbeResult(result domain.SCUMSchemaProbeResult) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "requestId", result.RequestID)
|
||||
@@ -59,6 +95,65 @@ func ValidateSCUMSchemaProbeResult(result domain.SCUMSchemaProbeResult) error {
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateSCUMSQLiteTemplateResult(result domain.SCUMSQLiteTemplateResult) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "requestId", result.RequestID)
|
||||
violations = appendRequired(violations, "jobId", result.JobID)
|
||||
violations = append(violations, validateSCUMBindingIdentity("binding", result.Binding)...)
|
||||
if !validSCUMTerminalResultStatus(result.Status) {
|
||||
violations = append(violations, "status is invalid")
|
||||
}
|
||||
if !validSCUMReadCapability(result.Capability) {
|
||||
violations = append(violations, "capability must be a read capability")
|
||||
}
|
||||
violations = append(violations, validateSCUMTemplateKey("targetKey", result.TargetKey)...)
|
||||
violations = append(violations, validateSCUMTemplateKey("templateKey", result.TemplateKey)...)
|
||||
violations = appendRequired(violations, "adapterVersion", result.AdapterVersion)
|
||||
if result.AdapterVersion != "" && containsSCUMProtectedMaterial(result.AdapterVersion) {
|
||||
violations = append(violations, "adapterVersion contains protected material")
|
||||
}
|
||||
if result.AdapterVersion != "" && result.Binding.AdapterVersion != "" && result.AdapterVersion != result.Binding.AdapterVersion {
|
||||
violations = append(violations, "adapterVersion must match binding.adapterVersion")
|
||||
}
|
||||
if result.SchemaFingerprint != "" && !validSCUMFingerprint(result.SchemaFingerprint) {
|
||||
violations = append(violations, "schemaFingerprint must be a digest/fingerprint")
|
||||
}
|
||||
if !validSCUMDigest(result.AssetDigest) {
|
||||
violations = append(violations, "assetDigest must be sha256 digest")
|
||||
}
|
||||
if !validSCUMDigest(result.ParameterDigest) {
|
||||
violations = append(violations, "parameterDigest must be sha256 digest")
|
||||
}
|
||||
if result.SourceFingerprint != "" && !validSCUMFingerprint(result.SourceFingerprint) {
|
||||
violations = append(violations, "sourceFingerprint must be a digest/fingerprint")
|
||||
}
|
||||
if !validSCUMFingerprint(result.ResultDigest) {
|
||||
violations = append(violations, "resultDigest must be a digest/fingerprint")
|
||||
}
|
||||
violations = append(violations, validateSCUMSafeError("safeError", result.SafeError)...)
|
||||
violations = append(violations, validateSCUMSQLiteTemplateBounds("limits", result.Limits)...)
|
||||
if result.RowCount != len(result.Rows) {
|
||||
violations = append(violations, "rowCount must match returned rows")
|
||||
}
|
||||
if len(result.Rows) > result.Limits.MaxRows && result.Limits.MaxRows > 0 {
|
||||
violations = append(violations, "rows exceeds declared limit")
|
||||
}
|
||||
for i, row := range result.Rows {
|
||||
violations = append(violations, validateSCUMValueMap(fmt.Sprintf("rows[%d]", i), row, 256)...)
|
||||
}
|
||||
if result.Status == domain.SCUMTerminalResultSucceeded {
|
||||
if result.SchemaFingerprint == "" || result.SourceFingerprint == "" {
|
||||
violations = append(violations, "succeeded result requires schema/source fingerprints")
|
||||
}
|
||||
if result.SafeError.Code != "" && result.SafeError.Code != domain.SCUMSafeErrorNone {
|
||||
violations = append(violations, "succeeded result must not carry an error code")
|
||||
}
|
||||
} else if result.SafeError.Code == "" || result.SafeError.Code == domain.SCUMSafeErrorNone {
|
||||
violations = append(violations, "non-succeeded result requires a safe error code")
|
||||
}
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateSCUMCapabilityEvidence(evidence domain.SCUMCapabilityEvidence) error {
|
||||
var violations []string
|
||||
if !validSCUMDataCapability(evidence.Capability) {
|
||||
@@ -213,6 +308,68 @@ func validateSCUMSchemaProbeBounds(prefix string, value domain.SCUMSchemaProbeBo
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateSCUMSQLiteTemplateBounds(prefix string, value domain.SCUMSQLiteTemplateBounds) []string {
|
||||
var violations []string
|
||||
if value.MaxParameters < 0 || value.MaxParameters > maxSCUMTemplateParameters {
|
||||
violations = append(violations, prefix+".maxParameters is out of bounds")
|
||||
}
|
||||
if value.MaxRows < 1 || value.MaxRows > maxSCUMTemplateRows {
|
||||
violations = append(violations, prefix+".maxRows is out of bounds")
|
||||
}
|
||||
if value.TimeoutMS < 1 || value.TimeoutMS > maxSCUMProbeTimeoutMS {
|
||||
violations = append(violations, prefix+".timeoutMs is out of bounds")
|
||||
}
|
||||
if value.BusyTimeoutMS < 0 || value.BusyTimeoutMS > maxSCUMTemplateBusyTimeoutMS {
|
||||
violations = append(violations, prefix+".busyTimeoutMs is out of bounds")
|
||||
}
|
||||
if value.MaxResultBytes < 1 || value.MaxResultBytes > maxSCUMProbeResultBytes {
|
||||
violations = append(violations, prefix+".maxResultBytes is out of bounds")
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateSCUMTemplateKey(prefix, value string) []string {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, prefix, value)
|
||||
if value != "" && (!scumTemplateKeyPattern.MatchString(value) || containsSCUMProtectedMaterial(value) || strings.Contains(value, "..")) {
|
||||
violations = append(violations, prefix+" is unsafe")
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateSCUMValueMap(prefix string, values map[string]any, maxItems int) []string {
|
||||
var violations []string
|
||||
if maxItems >= 0 && len(values) > maxItems {
|
||||
violations = append(violations, prefix+" exceeds declared limit")
|
||||
}
|
||||
for key, value := range values {
|
||||
if !scumTemplateKeyPattern.MatchString(key) || containsSCUMProtectedMaterial(key) || strings.Contains(key, "..") {
|
||||
violations = append(violations, prefix+" key is unsafe")
|
||||
}
|
||||
field := prefix + ".value"
|
||||
violations = append(violations, validateSCUMValue(field, value)...)
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateSCUMValue(prefix string, value any) []string {
|
||||
var violations []string
|
||||
switch item := value.(type) {
|
||||
case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||||
return nil
|
||||
case string:
|
||||
if len([]byte(item)) > maxSCUMTemplateValueBytes {
|
||||
violations = append(violations, prefix+" is too large")
|
||||
}
|
||||
if containsSCUMProtectedMaterial(item) {
|
||||
violations = append(violations, prefix+" contains protected material")
|
||||
}
|
||||
default:
|
||||
violations = append(violations, prefix+" must be a scalar value")
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateSCUMSchemaObjectEvidence(prefix string, value domain.SCUMSchemaObjectEvidence) []string {
|
||||
var violations []string
|
||||
if !validSCUMFingerprint(value.ObjectHash) {
|
||||
@@ -283,6 +440,24 @@ func validSCUMDataCapability(value domain.SCUMDataCapability) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func validSCUMReadCapability(value domain.SCUMDataCapability) bool {
|
||||
switch value {
|
||||
case domain.SCUMDataCapabilityPlayerRead, domain.SCUMDataCapabilityPlayerDetailRead, domain.SCUMDataCapabilitySquadRead, domain.SCUMDataCapabilitySquadMemberRead, domain.SCUMDataCapabilityVehicleRead, domain.SCUMDataCapabilityFlagRead, domain.SCUMDataCapabilityPositionRead:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func validSCUMTerminalResultStatus(value domain.SCUMTerminalResultStatus) bool {
|
||||
switch value {
|
||||
case domain.SCUMTerminalResultSucceeded, domain.SCUMTerminalResultFailed, domain.SCUMTerminalResultCancelled:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func validSCUMCapabilityEvidenceStatus(value domain.SCUMCapabilityEvidenceStatus) bool {
|
||||
switch value {
|
||||
case domain.SCUMCapabilityEvidenceMissing, domain.SCUMCapabilityEvidenceCompatible, domain.SCUMCapabilityEvidenceIncompatible, domain.SCUMCapabilityEvidenceFailed:
|
||||
@@ -303,7 +478,7 @@ func validSCUMSchemaProbeResultStatus(value domain.SCUMCapabilityEvidenceStatus)
|
||||
|
||||
func validSCUMSafeErrorCode(value domain.SCUMSafeErrorCode) bool {
|
||||
switch value {
|
||||
case "", domain.SCUMSafeErrorNone, domain.SCUMSafeErrorProbeExecutorAbsent, domain.SCUMSafeErrorProbeMissing, domain.SCUMSafeErrorProbeFailed, domain.SCUMSafeErrorSchemaIncompatible, domain.SCUMSafeErrorBindingMismatch, domain.SCUMSafeErrorAdapterMismatch, domain.SCUMSafeErrorFingerprintMismatch, domain.SCUMSafeErrorDigestMismatch, domain.SCUMSafeErrorEvidenceExpired, domain.SCUMSafeErrorInvalidProbePayload, domain.SCUMSafeErrorInvalidRequest, domain.SCUMSafeErrorTargetUnavailable, domain.SCUMSafeErrorSourceUnavailable, domain.SCUMSafeErrorSQLiteOpenFailed, domain.SCUMSafeErrorSQLiteReadFailed, domain.SCUMSafeErrorDatabaseBusy, domain.SCUMSafeErrorTimeout, domain.SCUMSafeErrorCancelled, domain.SCUMSafeErrorSourceChanged, domain.SCUMSafeErrorResultLimitExceeded:
|
||||
case "", domain.SCUMSafeErrorNone, domain.SCUMSafeErrorProbeExecutorAbsent, domain.SCUMSafeErrorProbeMissing, domain.SCUMSafeErrorProbeFailed, domain.SCUMSafeErrorSchemaIncompatible, domain.SCUMSafeErrorBindingMismatch, domain.SCUMSafeErrorAdapterMismatch, domain.SCUMSafeErrorFingerprintMismatch, domain.SCUMSafeErrorDigestMismatch, domain.SCUMSafeErrorEvidenceExpired, domain.SCUMSafeErrorInvalidProbePayload, domain.SCUMSafeErrorInvalidRequest, domain.SCUMSafeErrorTargetUnavailable, domain.SCUMSafeErrorSourceUnavailable, domain.SCUMSafeErrorSQLiteOpenFailed, domain.SCUMSafeErrorSQLiteReadFailed, domain.SCUMSafeErrorDatabaseBusy, domain.SCUMSafeErrorTimeout, domain.SCUMSafeErrorCancelled, domain.SCUMSafeErrorSourceChanged, domain.SCUMSafeErrorResultLimitExceeded, domain.SCUMSafeErrorTemplateMissing, domain.SCUMSafeErrorTemplateMismatch, domain.SCUMSafeErrorParameterInvalid, domain.SCUMSafeErrorRowLimitExceeded, domain.SCUMSafeErrorResultSchemaInvalid:
|
||||
return true
|
||||
default:
|
||||
return scumDataTargetSafeErrorPattern.MatchString(string(value))
|
||||
|
||||
Reference in New Issue
Block a user