From 9d0431f7d3625c3400d87972fcd02295886c4219 Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:45:48 +0800 Subject: [PATCH] fix(validation): preserve credential literal boundaries Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .../validation/BasicPrePublishValidator.java | 30 ++++++++++--------- .../BasicPrePublishValidatorTest.java | 30 ++++++++++++++++++- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidator.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidator.java index 8e8a914a..b31cdc07 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidator.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidator.java @@ -17,6 +17,7 @@ import java.util.regex.Pattern; @Component public class BasicPrePublishValidator implements PrePublishValidator { + private static final int MIN_GENERIC_SECRET_LENGTH = 12; private static final Pattern ASSIGNMENT_WITH_SENSITIVE_KEY = Pattern.compile( "(?i)(api[_-]?key|access[_-]?key|secret|password|token)\\s*[:=]\\s*(.+)$" ); @@ -111,7 +112,7 @@ public class BasicPrePublishValidator implements PrePublishValidator { String quotedLiteral = extractQuotedLiteral(rawValue); if (quotedLiteral != null) { - return quotedLiteral; + return quotedLiteral.length() >= MIN_GENERIC_SECRET_LENGTH ? quotedLiteral : null; } rawValue = stripInlineComment(rawValue); @@ -121,7 +122,7 @@ public class BasicPrePublishValidator implements PrePublishValidator { quotedLiteral = extractQuotedLiteral(rawValue); if (quotedLiteral != null) { - return quotedLiteral; + return quotedLiteral.length() >= MIN_GENERIC_SECRET_LENGTH ? quotedLiteral : null; } if (looksLikeExpression(rawValue) || IDENTIFIER.matcher(rawValue).matches()) { @@ -153,24 +154,25 @@ public class BasicPrePublishValidator implements PrePublishValidator { continue; } if (current == quote) { - return hasOnlyTrailingSyntax(rawValue, i + 1) ? rawValue.substring(1, i) : null; + return hasLiteralTerminator(rawValue, i + 1) ? rawValue.substring(1, i) : null; } } return null; } - private boolean hasOnlyTrailingSyntax(String rawValue, int startIndex) { - for (int i = startIndex; i < rawValue.length(); i++) { - char current = rawValue.charAt(i); - if (Character.isWhitespace(current) || isTrailingDelimiter(current)) { - continue; - } - if (current == '#') { - return true; - } - return current == '/' && i + 1 < rawValue.length() && rawValue.charAt(i + 1) == '/'; + private boolean hasLiteralTerminator(String rawValue, int startIndex) { + int index = startIndex; + while (index < rawValue.length() && Character.isWhitespace(rawValue.charAt(index))) { + index++; } - return true; + if (index == rawValue.length()) { + return true; + } + + char current = rawValue.charAt(index); + return isTrailingDelimiter(current) + || current == '#' + || (current == '/' && index + 1 < rawValue.length() && rawValue.charAt(index + 1) == '/'); } private boolean isTrailingDelimiter(char value) { diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidatorTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidatorTest.java index 0cca83f6..5610c8a8 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidatorTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/skill/validation/BasicPrePublishValidatorTest.java @@ -136,8 +136,10 @@ class BasicPrePublishValidatorTest { github_token = "ghp_abcdefghijklmnopqrstuvwxyz1234" const token = "javascriptcredential123"; const config = { token: "objectcredential123", }; + const options = { token: "multipropertycredential123", endpoint: "/api" }; + const escaped = { token: "credential\\\"value123" }; """.getBytes(StandardCharsets.UTF_8), - 184, + 319, "text/javascript" ); @@ -153,6 +155,32 @@ class BasicPrePublishValidatorTest { assertTrue(result.warnings().stream().anyMatch(warning -> warning.contains("line 2"))); assertTrue(result.warnings().stream().anyMatch(warning -> warning.contains("line 3"))); assertTrue(result.warnings().stream().anyMatch(warning -> warning.contains("line 4"))); + assertTrue(result.warnings().stream().anyMatch(warning -> warning.contains("line 5"))); + assertTrue(result.warnings().stream().anyMatch(warning -> warning.contains("line 6"))); + } + + @Test + void shouldNotWarnOnEmptyOrShortSensitiveLiterals() { + PackageEntry script = new PackageEntry( + "scripts/defaults.py", + """ + token = "" + client_secret = "short" + password = 'unset' + """.getBytes(StandardCharsets.UTF_8), + 58, + "text/x-python" + ); + + ValidationResult result = validator.validate(new PrePublishValidator.SkillPackageContext( + List.of(script), + new SkillMetadata("Defaults Skill", "desc", "1.0.0", "body", Map.of()), + "user-1", + 1L + )); + + assertTrue(result.passed()); + assertTrue(result.warnings().isEmpty()); } @Test