{"record":{"id":"5054947e1b50e2b5","repo":"iflytek/astron-agent","slug":"8132-workflow-artifact-content-type-mismatch","errorCode":"8132","errorMessage":"workflow.artifact.content.type.mismatch","messagePattern":"workflow\\.artifact\\.content\\.type\\.mismatch","errorType":"error_code","errorClass":"BusinessException","httpStatus":null,"severity":"warning","filePath":"console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowArtifactFileValidator.java","lineNumber":151,"sourceCode":"        if (file.getSize() <= 0 || file.getSize() > properties.getArtifactMaxFileSize().toBytes()) {\n            throw new BusinessException(ResponseEnum.WORKFLOW_ARTIFACT_FILE_TOO_LARGE);\n        }\n\n        String fileName = normalizeFileName(file.getOriginalFilename());\n        String extension = StringUtils.lowerCase(FilenameUtils.getExtension(fileName), Locale.ROOT);\n        Set<String> configuredExtensions = properties.getArtifactAllowedExtensions();\n        if (StringUtils.isBlank(extension)\n                || configuredExtensions.stream().noneMatch(extension::equalsIgnoreCase)\n                || !MEDIA_TYPES_BY_EXTENSION.containsKey(extension)) {\n            throw new BusinessException(ResponseEnum.WORKFLOW_ARTIFACT_FILE_TYPE_NOT_ALLOWED);\n        }\n\n        String declaredType = normalizeMediaType(file.getContentType());\n        if (ACTIVE_CONTENT_TYPES.contains(declaredType)\n                || (!StringUtils.isBlank(declaredType)\n                        && !OCTET_STREAM.equals(declaredType)\n                        && !MEDIA_TYPES_BY_EXTENSION.get(extension).contains(declaredType))) {\n            throw new BusinessException(ResponseEnum.WORKFLOW_ARTIFACT_CONTENT_TYPE_MISMATCH);\n        }\n\n        // Preflight Office containers before the general detector. OOXML resource limits run\n        // before its POI package parse inside validateOoxmlContainer.\n        validateOfficeContainer(file, extension);\n        String detectedType;\n        try (InputStream input = file.getInputStream()) {\n            detectedType = normalizeMediaType(tika.detect(input, fileName));\n        } catch (IOException exception) {\n            throw new BusinessException(ResponseEnum.WORKFLOW_ARTIFACT_CONTENT_TYPE_MISMATCH);\n        }\n        if (ACTIVE_CONTENT_TYPES.contains(detectedType)\n                || !MEDIA_TYPES_BY_EXTENSION.get(extension).contains(detectedType)) {\n            throw new BusinessException(ResponseEnum.WORKFLOW_ARTIFACT_CONTENT_TYPE_MISMATCH);\n        }\n        return new ValidatedArtifact(fileName, detectedType);\n    }\n","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowArtifactFileValidator.java#L133-L169","documentation":"Thrown when the multipart request's declared Content-Type header is active content (HTML/JS/SVG/XHTML) or, if non-blank and not application/octet-stream, does not belong to the allowed media-type set for the file's extension. This is a first-line sniff check on the client-declared type, done before any content inspection. It prevents browsers/clients from dressing up active payloads as innocuous extensions.","triggerScenarios":"validate() called where file.getContentType() returns e.g. text/html, image/svg+xml, application/javascript, text/javascript, or application/xhtml+xml; or a concrete non-octet-stream type not in MEDIA_TYPES_BY_EXTENSION.get(extension), e.g. a .txt upload declaring Content-Type: text/csv-x, or a .pdf upload declaring image/png.","commonSituations":"HTTP clients mislabeling uploads (wrong/bogus Content-Type header); proxies or gateways rewriting Content-Type; a generic 'application/octet-stream' is tolerated but a wrong specific type is not; Spring's multipart parsing propagating the client header verbatim.","solutions":["Fix the client or upload code so the declared Content-Type matches the file's actual type and is in the allowed set for that extension (see MEDIA_TYPES_BY_EXTENSION).","If the client cannot know the type, send Content-Type: application/octet-stream, which the validator deliberately tolerates and replaces with the Tika-detected type.","Verify no middleware (gateway, WAF, antivirus proxy) is rewriting the Content-Type header of multipart parts."],"exampleFix":"// before: client sends wrong type for a .md file\nContent-Type: text/html\n// after\nContent-Type: text/markdown  (or application/octet-stream)","handlingStrategy":"validation","validationCode":"String declared = StringUtils.lowerCase(StringUtils.substringBefore(file.getContentType(), \";\")).trim();\nboolean ok = declared.isEmpty() || declared.equals(\"application/octet-stream\")\n        || allowedMediaTypesForExtension(ext).contains(declared);\nif (!ok) { /* reject before validate() */ }","typeGuard":null,"tryCatchPattern":"try { validator.validate(file); } catch (BusinessException e) { /* map WORKFLOW_ARTIFACT_CONTENT_TYPE_MISMATCH to a 'declared Content-Type not allowed for this extension' response */ }","preventionTips":["Send application/octet-stream when the client cannot determine the type — the validator replaces it with server-side detection.","Never send text/html, application/javascript, image/svg+xml, or similar active types as the declared type.","Test uploads through your full proxy/gateway chain to confirm no middleware rewrites Content-Type."],"tags":["file-upload","content-type","validation","spring"],"backgroundTag":"type-mismatch","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}