Attachment Validation API

Last modified by Eleni Cojocariu on 2026/10/05 20:05

Reference

The Attachment Validation API turns an attachment down on the server, after the bytes have been received and before the wiki saves them. Client-Side Attachment Validation spares a reader the upload of a file that would be refused; this API is what a REST client running no JavaScript still has to pass.

xwiki-platform-attachment-validation-api declares the roles below and xwiki-platform-attachment-validation-default implements them and ships the two validation steps. Both come with the default flavor. The Attachment Validation Application, xwiki-platform-attachment-validation-ui, is a consumer of these roles and not their container. Sources: http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-attachment/xwiki-platform-attachment-validation.

Roles

TypePackageMembersPurpose
AttachmentValidatororg.xwiki.attachment.validationvalidateAttachment()The entry point. Code about to write an attachment calls this one method, and a thrown AttachmentValidationException is the refusal.
AttachmentValidationSteporg.xwiki.attachment.validationvalidate()One aspect of the attachment each.
AttachmentAccessWrapperorg.xwiki.attachmentgetSize(), getInputStream(), getFileName()What both roles are handed. A plain interface, not a component role: XWiki core implements it for a servlet Part (PartAttachmentAccessWrapper) and for an XWikiAttachment, and so does code validating something of its own.
AttachmentValidationExceptionorg.xwiki.attachment.validationFour payload fields, belowThe checked exception both roles throw.
AttachmentValidationConfigurationorg.xwiki.attachment.validationSee the Attachment Validation Configuration APIThe configured limits the shipped steps read.
@Role
public interface AttachmentValidator
{
    void validateAttachment(AttachmentAccessWrapper wrapper) throws AttachmentValidationException;
}

@Role
public interface AttachmentValidationStep
{
    void validate(AttachmentAccessWrapper wrapper) throws AttachmentValidationException;
}

Anything writing an attachment is expected to call validateAttachment first. XWiki's own paths already do: FileUploadUtils and FileUploadPlugin while an upload is parsed, BaseAttachmentsResource over REST, DefaultTemporaryAttachmentSessionsManager for the temporary attachments the editors hold.

The Order Steps Run In

The validator calls the size step, then the mimetype step, both hardcoded, and only then the remaining AttachmentValidationStep components, skipping those two. Size is first so that an oversized file is refused before anything pays for reading it. A step of your own always runs after both, in an order no annotation controls today.

The Shipped Steps

StepHintHTTP statusTranslation keyRefuses
File sizesize413attachment.validation.filesize.rejectedA file larger than the maximum attachment size of the Page, which it carries as its single translation parameter.
Mimetypemimetype415attachment.validation.mimetype.rejectedA file whose mimetype the allow and block lists turn down, both of which it carries as translation parameters.

The mimetype is detected with Apache Tika, from the stream and the file name, never from what the browser declared; a detection failure leaves an empty mimetype. The file is refused when an allow list is configured and the mimetype matches none of its patterns, or when a block list is configured and it matches one. An empty list counts as not configured.

A pattern with no * is an exact match against the lower-cased mimetype. The first *, and only the first, is a joker: what precedes it must be a prefix of the mimetype and what follows it a suffix, so image/* and */pdf work. It is neither a glob nor a regular expression.

Those values are administered elsewhere: see Set the Maximum Attachment Size and Restrict Attachments by Mimetype.

Refusing an Attachment

AttachmentValidationException carries the whole rejection, not just a message. Its four payload fields are what the upload paths turn into an answer, so a step that leaves them empty produces an untranslated, mis-statused rejection.

MemberCarries
getHttpStatus()The response status, 413 and 415 for the shipped steps.
getTranslationKey()The key of the message shown to the reader; the message itself is never in the exception.
getTranslationParameters()The values that key is rendered with, as a List<Object>.
getContextMessage()A key put in the XWiki context; only the size step sets one.

UploadAction puts them where the upload template finds them; AttachmentValidationExceptionMapper, a JAX-RS ExceptionMapper, answers a REST upload with a JSON body of message, translationKey and translationParameters under that same status.

When the Module Is Not Installed

The implementation is optional. XWiki core registers VoidAttachmentValidator, a no-op, at the lower priority 2000, so DefaultAttachmentValidator takes over where xwiki-platform-attachment-validation-default is installed and the role still resolves where it is not. A call that throws nothing is therefore no proof that the file was examined. The Attachment Validation Configuration API answers the same absence its own way.

Related

Get Connected