added optional web component UI control
This commit is contained in:
@@ -46,6 +46,66 @@ bool ParseTemporalHistorySource(const std::string& sourceName, TemporalHistorySo
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSafeUiEntryPath(const std::string& entryPath)
|
||||
{
|
||||
if (Trim(entryPath).empty())
|
||||
return false;
|
||||
if (entryPath.find('\\') != std::string::npos || entryPath.find(':') != std::string::npos ||
|
||||
entryPath.find('?') != std::string::npos || entryPath.find('#') != std::string::npos)
|
||||
return false;
|
||||
|
||||
const std::filesystem::path path(entryPath);
|
||||
if (path.empty() || path.is_absolute())
|
||||
return false;
|
||||
|
||||
bool firstPart = true;
|
||||
bool startsInUiDirectory = false;
|
||||
for (const std::filesystem::path& part : path)
|
||||
{
|
||||
if (part.empty() || part == "." || part == "..")
|
||||
return false;
|
||||
if (firstPart)
|
||||
{
|
||||
startsInUiDirectory = part == "ui";
|
||||
firstPart = false;
|
||||
}
|
||||
}
|
||||
if (!startsInUiDirectory)
|
||||
return false;
|
||||
|
||||
const std::filesystem::path normalized = path.lexically_normal();
|
||||
if (normalized.empty() || normalized.is_absolute())
|
||||
return false;
|
||||
for (const std::filesystem::path& part : normalized)
|
||||
{
|
||||
if (part.empty() || part == "." || part == "..")
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string extension = normalized.extension().string();
|
||||
return extension == ".js" || extension == ".mjs";
|
||||
}
|
||||
|
||||
bool IsValidCustomElementTag(const std::string& tag)
|
||||
{
|
||||
if (tag.empty() || tag.find('-') == std::string::npos || tag.front() == '-' || tag.back() == '-')
|
||||
return false;
|
||||
|
||||
const unsigned char first = static_cast<unsigned char>(tag.front());
|
||||
if (first < 'a' || first > 'z')
|
||||
return false;
|
||||
|
||||
for (char ch : tag)
|
||||
{
|
||||
const unsigned char value = static_cast<unsigned char>(ch);
|
||||
if ((value >= 'a' && value <= 'z') || (value >= '0' && value <= '9') || value == '-')
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ManifestPathMessage(const std::filesystem::path& manifestPath)
|
||||
@@ -367,4 +427,50 @@ bool ParseFeedbackSettings(const JsonValue& manifestJson, ShaderPackage& shaderP
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseUiDefinition(const JsonValue& manifestJson, ShaderPackage& shaderPackage, const std::filesystem::path& manifestPath, std::string& error)
|
||||
{
|
||||
const JsonValue* uiValue = nullptr;
|
||||
if (!OptionalObjectField(manifestJson, "ui", uiValue, manifestPath, error))
|
||||
return false;
|
||||
if (!uiValue)
|
||||
return true;
|
||||
|
||||
ShaderUiDefinition ui;
|
||||
if (!RequireNonEmptyStringField(*uiValue, "type", ui.type, manifestPath, error) ||
|
||||
!RequireNonEmptyStringField(*uiValue, "entry", ui.entryPath, manifestPath, error) ||
|
||||
!RequireNonEmptyStringField(*uiValue, "tag", ui.customElementTag, manifestPath, error))
|
||||
{
|
||||
error = "Shader UI definition is missing required 'type', 'entry', or 'tag' in: " + ManifestPathMessage(manifestPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ui.type != "webComponent")
|
||||
{
|
||||
error = "Shader UI type must be 'webComponent' in: " + ManifestPathMessage(manifestPath);
|
||||
return false;
|
||||
}
|
||||
if (!IsSafeUiEntryPath(ui.entryPath))
|
||||
{
|
||||
error = "Shader UI entry must be a safe relative .js or .mjs path under ui/ in: " + ManifestPathMessage(manifestPath);
|
||||
return false;
|
||||
}
|
||||
if (!IsValidCustomElementTag(ui.customElementTag))
|
||||
{
|
||||
error = "Shader UI tag must be a valid custom element name with a hyphen in: " + ManifestPathMessage(manifestPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path entryPath = shaderPackage.directoryPath / std::filesystem::path(ui.entryPath);
|
||||
if (!std::filesystem::exists(entryPath))
|
||||
{
|
||||
error = "Shader UI entry not found for package " + shaderPackage.id + ": " + entryPath.string();
|
||||
return false;
|
||||
}
|
||||
|
||||
ui.entryPath = std::filesystem::path(ui.entryPath).lexically_normal().generic_string();
|
||||
ui.enabled = true;
|
||||
shaderPackage.ui = ui;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user