Check the canvas size before narrowing it to an int
A canvas side was cast to int with no range check, so a number that did not fit came back as one that did: `-s 4294968296x600` rendered a perfectly plausible 1000x600, and a hand-edited project file could ask for a width the reader turned into something else (or, for a double out of range, into undefined behaviour). Both front-ends now check the side before the cast, against one shared bound in chain.hpp — the 1..20000 the GUI spin buttons already offered, so the help text, the CLI error, the project reader and the interface all move together. The tests gain a `canvas size` section covering the round trip at both ends of the range and the refusals on either side of it, through the project reader and through `-s`; the test binary picks up the CLI parser to do it, which stays GUI-free. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+17
-3
@@ -283,6 +283,21 @@ std::string relativeTo(const std::string& path, const std::string& directory) {
|
||||
return text.rfind("..", 0) == 0 ? path : text;
|
||||
}
|
||||
|
||||
// A canvas side is checked as a double, before it is narrowed: casting a
|
||||
// number outside the range of an int is undefined, and even where it wraps
|
||||
// quietly it turns a size the file never named into one that looks ordinary.
|
||||
int canvasSide(const Json& value, const char* what) {
|
||||
if (value.kind != Json::Kind::Number || value.number != std::trunc(value.number)) {
|
||||
throw EffectError(std::string("the canvas ") + what + " must be a whole number");
|
||||
}
|
||||
if (!(value.number >= kMinCanvasSide) || !(value.number <= kMaxCanvasSide)) {
|
||||
throw EffectError(std::string("the canvas ") + what + " must be between " +
|
||||
std::to_string(kMinCanvasSide) + " and " +
|
||||
std::to_string(kMaxCanvasSide) + " pixels");
|
||||
}
|
||||
return static_cast<int>(value.number);
|
||||
}
|
||||
|
||||
std::string resolve(const std::string& path, const std::string& directory) {
|
||||
std::filesystem::path file(path);
|
||||
if (file.is_absolute() || directory.empty()) return path;
|
||||
@@ -350,12 +365,11 @@ Project projectFromJson(const std::string& text, const std::string& directory) {
|
||||
Project project;
|
||||
|
||||
if (const auto* canvas = root.member("canvas")) {
|
||||
if (const auto* width = canvas->member("width")) project.width = static_cast<int>(width->number);
|
||||
if (const auto* width = canvas->member("width")) project.width = canvasSide(*width, "width");
|
||||
if (const auto* height = canvas->member("height")) {
|
||||
project.height = static_cast<int>(height->number);
|
||||
project.height = canvasSide(*height, "height");
|
||||
}
|
||||
}
|
||||
if (project.width <= 0 || project.height <= 0) throw EffectError("the canvas size is not positive");
|
||||
|
||||
if (const auto* seed = root.member("seed")) {
|
||||
if (seed->kind == Json::Kind::Number) {
|
||||
|
||||
Reference in New Issue
Block a user