Skip to content

Instantly share code, notes, and snippets.

@zygoat
Created January 18, 2024 21:53
Show Gist options
  • Save zygoat/5d688a22be0542156b88eba3f14ec9ad to your computer and use it in GitHub Desktop.
Save zygoat/5d688a22be0542156b88eba3f14ec9ad to your computer and use it in GitHub Desktop.
/// A middlware that filters a request body of type `application/x-www-form-urlencoded`
/// removing any key/value pairs having a zero-length value.
///
/// This is a remediation for a design defect where the presence of any such null entries
/// will cause model decoding to fail. See https://github.com/vapor/vapor/issues/3046.
struct URLEncodedFormSanitizingMiddlware: AsyncMiddleware {
func respond(to request: Request,
chainingTo next: AsyncResponder) async throws -> Response {
// Read the entire body content, which might not yet be available.
let bodyString: String?
if let string = request.body.string {
bodyString = string
} else {
var bodyBuffer = try await request.body.collect(upTo: Int.max)
bodyString = bodyBuffer.readString(length: bodyBuffer.readableBytes)
}
guard request.content.contentType == .urlEncodedForm,
let bodyString else {
// Pass through unmodified.
return try await next.respond(to: request)
}
// Strip out any null-value entries.
let cleanedString = bodyString.replacing(try Regex("[A-Za-z0-9_]+=(&|$)"), with: "")
// Contrive a replacement request in the image of the original.
let newRequest = Request(application: request.application,
method: request.method,
url: request.url,
version: request.version,
headers: request.headers,
collectedBody: ByteBuffer(string: cleanedString),
remoteAddress: request.remoteAddress,
logger: request.logger,
byteBufferAllocator: request.byteBufferAllocator,
on: request.eventLoop)
return try await next.respond(to: newRequest)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment