mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-10 03:27:54 +00:00
fix(security): enforce YAML fallback limits
Signed-off-by: dongmucat <1127093059@qq.com>
This commit is contained in:
parent
f4fec3fe22
commit
4ff546b23d
2 changed files with 87 additions and 0 deletions
|
|
@ -62,6 +62,7 @@ public class SkillMetadataParser {
|
|||
}
|
||||
|
||||
private Map<String, Object> parseFrontmatter(String yamlContent) {
|
||||
validateFrontmatterCodePointLimit(yamlContent);
|
||||
if (containsExplicitYamlTag(yamlContent)) {
|
||||
throw new DomainBadRequestException("error.skill.metadata.yaml.invalid", "Explicit YAML tags are not allowed");
|
||||
}
|
||||
|
|
@ -77,6 +78,9 @@ public class SkillMetadataParser {
|
|||
} catch (DomainBadRequestException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
if (isLoaderConstraintException(exception)) {
|
||||
throw new DomainBadRequestException("error.skill.metadata.yaml.invalid", exception.getMessage());
|
||||
}
|
||||
Map<String, Object> fallback = parseLooseFrontmatter(yamlContent);
|
||||
if (!fallback.isEmpty()) {
|
||||
return fallback;
|
||||
|
|
@ -93,6 +97,25 @@ public class SkillMetadataParser {
|
|||
return new Yaml(new SafeConstructor(options));
|
||||
}
|
||||
|
||||
private void validateFrontmatterCodePointLimit(String yamlContent) {
|
||||
if (yamlContent.codePointCount(0, yamlContent.length()) > FRONTMATTER_CODE_POINT_LIMIT) {
|
||||
throw new DomainBadRequestException(
|
||||
"error.skill.metadata.yaml.invalid",
|
||||
"Frontmatter exceeds the supported size"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLoaderConstraintException(Exception exception) {
|
||||
String message = exception.getMessage();
|
||||
if (message == null) {
|
||||
return false;
|
||||
}
|
||||
return message.contains("exceeds the limit")
|
||||
|| message.contains("Nesting Depth exceeded")
|
||||
|| message.contains("found duplicate key");
|
||||
}
|
||||
|
||||
private boolean containsExplicitYamlTag(String yamlContent) {
|
||||
return yamlContent.lines()
|
||||
.map(String::trim)
|
||||
|
|
@ -231,6 +254,12 @@ public class SkillMetadataParser {
|
|||
if (key.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (values.containsKey(key)) {
|
||||
throw new DomainBadRequestException(
|
||||
"error.skill.metadata.yaml.invalid",
|
||||
"Duplicate YAML keys are not allowed"
|
||||
);
|
||||
}
|
||||
|
||||
values.put(key, stripWrappingQuotes(value));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,64 @@ class SkillMetadataParserTest {
|
|||
assertEquals("1.0.0", metadata.version());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsOversizedFrontmatterInsteadOfFallingBackToLooseParsing() {
|
||||
String longDescription = "x".repeat(70_000);
|
||||
String content = """
|
||||
---
|
||||
name: oversized-skill
|
||||
description: [unclosed bracket %s
|
||||
version: 1.0.0
|
||||
---
|
||||
Body
|
||||
""".formatted(longDescription);
|
||||
|
||||
DomainBadRequestException exception = assertThrows(
|
||||
DomainBadRequestException.class,
|
||||
() -> parser.parse(content)
|
||||
);
|
||||
assertEquals("error.skill.metadata.yaml.invalid", exception.messageCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsDuplicateKeysInsteadOfFallingBackToLooseParsing() {
|
||||
String content = """
|
||||
---
|
||||
name: original-skill
|
||||
name: overwritten-skill
|
||||
description: Duplicate keys should not be accepted
|
||||
version: 1.0.0
|
||||
---
|
||||
Body
|
||||
""";
|
||||
|
||||
DomainBadRequestException exception = assertThrows(
|
||||
DomainBadRequestException.class,
|
||||
() -> parser.parse(content)
|
||||
);
|
||||
assertEquals("error.skill.metadata.yaml.invalid", exception.messageCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsExcessiveNestingInsteadOfFallingBackToLooseParsing() {
|
||||
String nestedValue = "[".repeat(25) + "value" + "]".repeat(25);
|
||||
String content = """
|
||||
---
|
||||
name: deeply-nested-skill
|
||||
description: Deeply nested frontmatter should not be accepted
|
||||
metadata: %s
|
||||
version: 1.0.0
|
||||
---
|
||||
Body
|
||||
""".formatted(nestedValue);
|
||||
|
||||
DomainBadRequestException exception = assertThrows(
|
||||
DomainBadRequestException.class,
|
||||
() -> parser.parse(content)
|
||||
);
|
||||
assertEquals("error.skill.metadata.yaml.invalid", exception.messageCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsExplicitYamlTagsInsteadOfFallingBackToLooseParsing() {
|
||||
String content = """
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue