mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-28 11:25:00 +00:00
fix(ISSUE-64): restrict compliance evidenceUrl schemes
Signed-off-by: dongmucat <1127093059@qq.com>
This commit is contained in:
parent
1b5a8ab769
commit
eb2db152fd
3 changed files with 67 additions and 5 deletions
|
|
@ -5,12 +5,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
|
|
@ -23,6 +23,8 @@ public class SkillComplianceMetadataService {
|
|||
private static final int MAX_STANDARD_VERSION_LENGTH = 32;
|
||||
private static final int MAX_CONTROL_TITLE_LENGTH = 200;
|
||||
private static final Pattern CONTROL_ID_PATTERN = Pattern.compile("^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$");
|
||||
private static final Set<String> ALLOWED_EVIDENCE_URL_SCHEMES = Set.of("http", "https");
|
||||
private static final String EVIDENCE_URL_ERROR = "evidenceUrl must use an http or https URI";
|
||||
private static final Set<String> ALLOWED_KEYS = Set.of(
|
||||
"standard",
|
||||
"standardVersion",
|
||||
|
|
@ -221,18 +223,21 @@ public class SkillComplianceMetadataService {
|
|||
return null;
|
||||
}
|
||||
if (!(rawValue instanceof String value) || value.isBlank()) {
|
||||
errors.add("x-astron-compliance[" + index + "].evidenceUrl must be an absolute URI");
|
||||
errors.add("x-astron-compliance[" + index + "]." + EVIDENCE_URL_ERROR);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
URI uri = URI.create(value.trim());
|
||||
if (!uri.isAbsolute()) {
|
||||
errors.add("x-astron-compliance[" + index + "].evidenceUrl must be an absolute URI");
|
||||
String scheme = uri.getScheme();
|
||||
if (!uri.isAbsolute()
|
||||
|| scheme == null
|
||||
|| !ALLOWED_EVIDENCE_URL_SCHEMES.contains(scheme.toLowerCase(Locale.ROOT))) {
|
||||
errors.add("x-astron-compliance[" + index + "]." + EVIDENCE_URL_ERROR);
|
||||
return null;
|
||||
}
|
||||
return uri.toString();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
errors.add("x-astron-compliance[" + index + "].evidenceUrl must be an absolute URI");
|
||||
errors.add("x-astron-compliance[" + index + "]." + EVIDENCE_URL_ERROR);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,35 @@ class SkillComplianceMetadataServiceTest {
|
|||
assertThat(result.errors()).contains("x-astron-compliance must be a non-empty array");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseFrontmatter_rejectsUnsafeEvidenceUrlSchemes() {
|
||||
Map<String, Object> frontmatter = Map.of(
|
||||
"x-astron-compliance",
|
||||
List.of(
|
||||
Map.of(
|
||||
"standard", "gdpr",
|
||||
"standardVersion", "2024",
|
||||
"controlId", "Article-17",
|
||||
"evidenceUrl", "javascript:alert(1)"
|
||||
),
|
||||
Map.of(
|
||||
"standard", "soc2",
|
||||
"standardVersion", "2017",
|
||||
"controlId", "CC6.1",
|
||||
"evidenceUrl", "data:text/html;base64,SGk="
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
SkillComplianceMetadataService.ParseResult result = service.parseFrontmatter(frontmatter);
|
||||
|
||||
assertThat(result.mappings()).isEmpty();
|
||||
assertThat(result.errors()).containsExactlyInAnyOrder(
|
||||
"x-astron-compliance[0].evidenceUrl must use an http or https URI",
|
||||
"x-astron-compliance[1].evidenceUrl must use an http or https URI"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readFromParsedMetadataJson_extractsMappingsFromStoredSkillMetadata() {
|
||||
String parsedMetadataJson = """
|
||||
|
|
|
|||
|
|
@ -213,6 +213,34 @@ class SkillPackageValidatorTest {
|
|||
&& error.contains("x-astron-compliance must be a non-empty array")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnsafeComplianceEvidenceUrlRejected() {
|
||||
String skillMdContent = """
|
||||
---
|
||||
name: compliance-skill
|
||||
description: Skill with unsafe evidence url
|
||||
version: 1.0.0
|
||||
x-astron-compliance:
|
||||
- standard: gdpr
|
||||
standardVersion: "2024"
|
||||
controlId: Article-17
|
||||
evidenceUrl: "javascript:alert(1)"
|
||||
---
|
||||
Body
|
||||
""";
|
||||
|
||||
List<PackageEntry> entries = List.of(
|
||||
new PackageEntry("SKILL.md", skillMdContent.getBytes(), skillMdContent.length(), "text/markdown")
|
||||
);
|
||||
|
||||
ValidationResult result = validator.validate(entries);
|
||||
|
||||
assertFalse(result.passed());
|
||||
assertTrue(result.errors().stream().anyMatch(error ->
|
||||
error.contains("Invalid SKILL.md frontmatter")
|
||||
&& error.contains("evidenceUrl must use an http or https URI")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPackageTooLarge() {
|
||||
// Use a custom validator with 2KB total limit to test the logic
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue