Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion vendor/wheels/events/EventMethods.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,10 @@ component extends="wheels.Global" implements="wheels.interfaces.events.EventHand
public string function $getRequestFormat() {
local.rv = "html";
if (StructKeyExists(url, "format")) {
local.rv = url.format;
// Security: reject non-alphanumeric url.format to prevent LFI via the error-template include path in $runOnError.
if (ReFind("^[A-Za-z0-9]+$", url.format)) {
local.rv = url.format;
}
} else if ((StructKeyExists(request, "cgi") && StructKeyExists(request.cgi, "http_accept"))){
local.httpAccept = request.cgi.http_accept;
local.formats = $get("formats");
Expand Down
42 changes: 42 additions & 0 deletions vendor/wheels/tests/specs/events/onerrorSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,48 @@ component extends="wheels.WheelsTest" {
expect($expectedStatusFor("Wheels.ActionParameterMissing")).toBe(500)
})
})

// Security regression: $getRequestFormat must reject non-alphanumeric url.format (LFI via $runOnError's error-template include path).
describe("$getRequestFormat rejects unsafe format tokens (T4 LFI)", () => {

it("coerces ../ traversal tokens to html", () => {
expect($requestFormatFor("../../../wheels/public/layout/_header_simple")).toBe("html")
})

it("coerces tokens containing a slash or dot to html", () => {
expect($requestFormatFor("onerror.cfm/../x")).toBe("html")
})

it("preserves a valid alphanumeric format", () => {
expect($requestFormatFor("json")).toBe("json")
})

it("preserves another valid format", () => {
expect($requestFormatFor("xml")).toBe("xml")
})

it("falls back to html for an empty format", () => {
expect($requestFormatFor("")).toBe("html")
})
})
}

private string function $requestFormatFor(required string formatValue) {
var em = CreateObject("component", "wheels.events.EventMethods")
var hadFormat = StructKeyExists(url, "format")
var prior = hadFormat ? url.format : ""
var result = ""
try {
url.format = arguments.formatValue
result = em.$getRequestFormat()
} finally {
if (hadFormat) {
url.format = prior
} else {
StructDelete(url, "format")
}
}
return result
}

private numeric function $expectedStatusFor(required string wheelsType) {
Expand Down
Loading