浏览代码

SONARCLOUD-628 Allow UTF-8 characters in project key

tags/7.8
Benoît Gianinetti 5 年前
父节点
当前提交
bf7776ff7d

+ 1
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterDao.java 查看文件

private static void checkNewNameOfAllModules(Set<ResourceDto> modules, String stringToReplace, String replacementString, ComponentKeyUpdaterMapper mapper) { private static void checkNewNameOfAllModules(Set<ResourceDto> modules, String stringToReplace, String replacementString, ComponentKeyUpdaterMapper mapper) {
for (ResourceDto module : modules) { for (ResourceDto module : modules) {
String newKey = computeNewKey(module.getKey(), stringToReplace, replacementString); String newKey = computeNewKey(module.getKey(), stringToReplace, replacementString);
checkArgument(isValidProjectKey(newKey), "Malformed key for '%s'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", newKey);
checkProjectKey(newKey);
if (mapper.countResourceByKey(newKey) > 0) { if (mapper.countResourceByKey(newKey) > 0) {
throw new IllegalArgumentException("Impossible to update key: a component with key \"" + newKey + "\" already exists."); throw new IllegalArgumentException("Impossible to update key: a component with key \"" + newKey + "\" already exists.");
} }

+ 3
- 3
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentKeyUpdaterDaoTest.java 查看文件

ComponentDto project = db.components().insertPrivateProject(); ComponentDto project = db.components().insertPrivateProject();


thrown.expect(IllegalArgumentException.class); thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Malformed key for 'my?project?key'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
thrown.expectMessage("Malformed key for ' '. Project key cannot be empty nor contain whitespaces.");


underTest.bulkUpdateKey(dbSession, project.uuid(), project.getDbKey(), "my?project?key", doNotReturnAnyRekeyedResource());
underTest.bulkUpdateKey(dbSession, project.uuid(), project.getDbKey(), " ", doNotReturnAnyRekeyedResource());
} }


@Test @Test


thrown.expect(IllegalArgumentException.class); thrown.expect(IllegalArgumentException.class);


underTest.simulateBulkUpdateKey(dbSession, "A", "project", "project?");
underTest.simulateBulkUpdateKey(dbSession, "A", "project", " ");
} }


@Test @Test

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java 查看文件

} }


private static void checkProjectOrModuleKeyFormat(String key) { private static void checkProjectOrModuleKeyFormat(String key) {
checkRequest(isValidProjectKey(key), "Malformed key for '%s'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", key);
checkRequest(isValidProjectKey(key), "Malformed key for '%s'. It cannot be empty nor contain whitespaces.", key);
} }


} }

+ 1
- 2
server/sonar-server/src/main/java/org/sonar/server/component/ComponentUpdater.java 查看文件

} }


private void checkKeyFormat(String qualifier, String key) { private void checkKeyFormat(String qualifier, String key) {
checkRequest(isValidProjectKey(key),
"Malformed key for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", getQualifierToDisplay(qualifier), key);
checkRequest(isValidProjectKey(key), "Malformed key for %s: '%s'. It cannot be empty nor contain whitespaces.", getQualifierToDisplay(qualifier), key);
} }


private void checkLegacyBranchFormat(String qualifier, @Nullable String branch) { private void checkLegacyBranchFormat(String qualifier, @Nullable String branch) {

+ 4
- 4
server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java 查看文件

logInAsProjectAdministrator(project); logInAsProjectAdministrator(project);


expectedException.expect(BadRequestException.class); expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Malformed key for ''. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
expectedException.expectMessage("Malformed key for ''. It cannot be empty nor contain whitespaces.");


underTest.updateKey(dbSession, project, ""); underTest.updateKey(dbSession, project, "");
} }


@Test @Test
public void fail_if_new_key_is_not_formatted_correctly() {
public void fail_if_new_key_is_invalid() {
ComponentDto project = insertSampleRootProject(); ComponentDto project = insertSampleRootProject();
logInAsProjectAdministrator(project); logInAsProjectAdministrator(project);


expectedException.expect(BadRequestException.class); expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Malformed key for 'sample?root'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
expectedException.expectMessage("Malformed key for 'sample root'. It cannot be empty nor contain whitespaces.");


underTest.updateKey(dbSession, project, "sample?root");
underTest.updateKey(dbSession, project, "sample root");
} }


@Test @Test

+ 4
- 4
server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java 查看文件

@Test @Test
public void fail_when_key_has_bad_format() { public void fail_when_key_has_bad_format() {
expectedException.expect(BadRequestException.class); expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Malformed key for Project: 1234");
expectedException.expectMessage("Malformed key for Project: ' '");


underTest.create(db.getSession(), underTest.create(db.getSession(),
NewComponent.newComponentBuilder() NewComponent.newComponentBuilder()
.setKey("1234")
.setKey(" ")
.setName(DEFAULT_PROJECT_NAME) .setName(DEFAULT_PROJECT_NAME)
.setOrganizationUuid(db.getDefaultOrganization().getUuid()) .setOrganizationUuid(db.getDefaultOrganization().getUuid())
.build(), .build(),
@Test @Test
public void properly_fail_when_key_contains_percent_character() { public void properly_fail_when_key_contains_percent_character() {
expectedException.expect(BadRequestException.class); expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Malformed key for Project: project%Key");
expectedException.expectMessage("Malformed key for Project: ' '");


underTest.create(db.getSession(), underTest.create(db.getSession(),
NewComponent.newComponentBuilder() NewComponent.newComponentBuilder()
.setKey("project%Key")
.setKey(" ")
.setName(DEFAULT_PROJECT_NAME) .setName(DEFAULT_PROJECT_NAME)
.setOrganizationUuid(db.getDefaultOrganization().getUuid()) .setOrganizationUuid(db.getDefaultOrganization().getUuid())
.build(), .build(),

+ 4
- 4
server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkUpdateKeyActionTest.java 查看文件

insertMyProject(); insertMyProject();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
expectedException.expectMessage("Malformed key for 'my aproject'. Project key cannot be empty nor contain whitespaces.");


callByKey(MY_PROJECT_KEY, FROM, "my?");
callByKey(MY_PROJECT_KEY, FROM, "my a");
} }


@Test @Test
insertMyProject(); insertMyProject();


expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
expectedException.expectMessage("Malformed key for 'my aproject'. Project key cannot be empty nor contain whitespaces.");


callDryRunByKey(MY_PROJECT_KEY, FROM, "my?");
callDryRunByKey(MY_PROJECT_KEY, FROM, "my a");
} }


@Test @Test

+ 3
- 3
server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java 查看文件

} }


@Test @Test
public void properly_fail_when_project_key_contains_percent_character() {
public void properly_fail_when_invalid_project_key() {
userSession.addPermission(PROVISION_PROJECTS, db.getDefaultOrganization()); userSession.addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());


expectedException.expect(BadRequestException.class); expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Malformed key for Project: project%Key. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
expectedException.expectMessage("Malformed key for Project: 'project Key'. It cannot be empty nor contain whitespaces.");


call(CreateRequest.builder() call(CreateRequest.builder()
.setKey("project%Key")
.setKey("project Key")
.setName(DEFAULT_PROJECT_NAME) .setName(DEFAULT_PROJECT_NAME)
.build()); .build());
} }

+ 19
- 17
sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java 查看文件

public static final int MAX_COMPONENT_KEY_LENGTH = 400; public static final int MAX_COMPONENT_KEY_LENGTH = 400;


/* /*
* Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit
* Must not be blank or empty
*/ */
private static final String VALID_PROJECT_KEY_REGEXP = "[\\p{Alnum}\\-_.:]*[\\p{Alpha}\\-_.:]+[\\p{Alnum}\\-_.:]*";
private static final String VALID_PROJECT_KEY_REGEXP = "[^\\p{javaWhitespace}]+";


private static final String VALID_PROJECT_KEY_ISSUES_MODE_REGEXP = "[\\p{Alnum}\\-_.:/]*[\\p{Alpha}\\-_.:/]+[\\p{Alnum}\\-_.:/]*"; private static final String VALID_PROJECT_KEY_ISSUES_MODE_REGEXP = "[\\p{Alnum}\\-_.:/]*[\\p{Alpha}\\-_.:/]+[\\p{Alnum}\\-_.:/]*";

/* /*
* Allowed characters are alphanumeric, '-', '_', '.' and '/' * Allowed characters are alphanumeric, '-', '_', '.' and '/'
*/ */
} }


/** /**
* <p>Test if given parameter is valid for a project. Valid format is:</p>
* <ul>
* <li>Allowed characters:
* <ul>
* <li>Uppercase ASCII letters A-Z</li>
* <li>Lowercase ASCII letters a-z</li>
* <li>ASCII digits 0-9</li>
* <li>Punctuation signs dash '-', underscore '_', period '.' and colon ':'</li>
* </ul>
* </li>
* <li>At least one non-digit</li>
* </ul>
* Test if given parameter is valid for a project. A key is valid if it doesn't contain whitespaces.
* *
* @return <code>true</code> if <code>keyCandidate</code> can be used for a project * @return <code>true</code> if <code>keyCandidate</code> can be used for a project
*/ */
* @throws IllegalArgumentException if the format is incorrect * @throws IllegalArgumentException if the format is incorrect
*/ */
public static void checkProjectKey(String keyCandidate) { public static void checkProjectKey(String keyCandidate) {
checkArgument(isValidProjectKey(keyCandidate), "Malformed key for '%s'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.",
keyCandidate);
checkArgument(isValidProjectKey(keyCandidate), "Malformed key for '%s'. %s", keyCandidate, "Project key cannot be empty nor contain whitespaces.");
} }


/** /**
* Same as {@link #isValidProjectKey(String)}, but allows additionally '/'.
* <p>Test if given parameter is valid for a project. Valid format is:</p>
* <ul>
* <li>Allowed characters:
* <ul>
* <li>Uppercase ASCII letters A-Z</li>
* <li>Lowercase ASCII letters a-z</li>
* <li>ASCII digits 0-9</li>
* <li>Punctuation signs dash '-', underscore '_', period '.', colon ':' and slash '/'</li>
* </ul>
* </li>
* <li>At least one non-digit</li>
* </ul>
*
* @return <code>true</code> if <code>keyCandidate</code> can be used for a project in issues mode
*/ */
public static boolean isValidProjectKeyIssuesMode(String keyCandidate) { public static boolean isValidProjectKeyIssuesMode(String keyCandidate) {
return keyCandidate.matches(VALID_PROJECT_KEY_ISSUES_MODE_REGEXP); return keyCandidate.matches(VALID_PROJECT_KEY_ISSUES_MODE_REGEXP);

+ 12
- 23
sonar-core/src/test/java/org/sonar/core/component/ComponentKeysTest.java 查看文件

} }


@Test @Test
public void isValidModuleKey() {
assertThat(ComponentKeys.isValidProjectKey("")).isFalse();
public void isValidProjectKey() {
assertThat(ComponentKeys.isValidProjectKey("abc")).isTrue(); assertThat(ComponentKeys.isValidProjectKey("abc")).isTrue();
assertThat(ComponentKeys.isValidProjectKey("0123")).isFalse();
assertThat(ComponentKeys.isValidProjectKey("ab 12")).isFalse();
assertThat(ComponentKeys.isValidProjectKey("0123")).isTrue();
assertThat(ComponentKeys.isValidProjectKey("ab_12")).isTrue(); assertThat(ComponentKeys.isValidProjectKey("ab_12")).isTrue();
assertThat(ComponentKeys.isValidProjectKey("ab/12")).isFalse();
assertThat(ComponentKeys.isValidProjectKey("ab/12")).isTrue();
assertThat(ComponentKeys.isValidProjectKey("코드품질")).isTrue();
assertThat(ComponentKeys.isValidProjectKey("")).isFalse();
assertThat(ComponentKeys.isValidProjectKey(" ")).isFalse();
assertThat(ComponentKeys.isValidProjectKey("ab 12")).isFalse();
assertThat(ComponentKeys.isValidProjectKey(" ab")).isFalse();
assertThat(ComponentKeys.isValidProjectKey("ab ")).isFalse();
} }


@Test @Test
} }


@Test @Test
public void checkModuleKey_with_correct_keys() {
public void checkProjectKey_with_correct_keys() {
ComponentKeys.checkProjectKey("abc"); ComponentKeys.checkProjectKey("abc");
ComponentKeys.checkProjectKey("a-b_1.:2"); ComponentKeys.checkProjectKey("a-b_1.:2");
} }


@Test @Test
public void checkModuleKey_fail_if_only_digit() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Malformed key for '0123'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");

ComponentKeys.checkProjectKey("0123");
}

@Test
public void checkModuleKey_fail_if_key_is_empty() {
public void checkProjectKey_fail_if_key_is_empty() {
expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);


ComponentKeys.checkProjectKey(""); ComponentKeys.checkProjectKey("");
} }


@Test @Test
public void checkModuleKey_fail_if_space() {
public void checkProjectKey_fail_if_space() {
expectedException.expect(IllegalArgumentException.class); expectedException.expect(IllegalArgumentException.class);


ComponentKeys.checkProjectKey("ab 12"); ComponentKeys.checkProjectKey("ab 12");
} }

@Test
public void checkModuleKey_fail_if_special_characters_not_allowed() {
expectedException.expect(IllegalArgumentException.class);

ComponentKeys.checkProjectKey("ab/12");
}
} }

+ 1
- 2
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java 查看文件



private static void validateModule(ProjectDefinition moduleDef, List<String> validationMessages) { private static void validateModule(ProjectDefinition moduleDef, List<String> validationMessages) {
if (!ComponentKeys.isValidProjectKey(moduleDef.getKey())) { if (!ComponentKeys.isValidProjectKey(moduleDef.getKey())) {
validationMessages.add(format("\"%s\" is not a valid project or module key. "
+ "Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", moduleDef.getKey()));
validationMessages.add(format("\"%s\" is not a valid project or module key. It cannot be empty nor contain whitespaces.", moduleDef.getKey()));
} }
} }



+ 2
- 26
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java 查看文件

public void allow_slash_issues_mode() { public void allow_slash_issues_mode() {
when(mode.isIssues()).thenReturn(true); when(mode.isIssues()).thenReturn(true);
underTest.validate(createProjectReactor("project/key")); underTest.validate(createProjectReactor("project/key"));

when(mode.isIssues()).thenReturn(false);
thrown.expect(MessageException.class);
thrown.expectMessage("is not a valid project or module key");
underTest.validate(createProjectReactor("project/key"));
} }


@Test @Test
public void fail_with_invalid_key() { public void fail_with_invalid_key() {
ProjectReactor reactor = createProjectReactor("foo$bar");

thrown.expect(MessageException.class);
thrown.expectMessage("\"foo$bar\" is not a valid project or module key");
underTest.validate(reactor);
}

@Test
public void fail_with_backslash_in_key() {
ProjectReactor reactor = createProjectReactor("foo\\bar");
ProjectReactor reactor = createProjectReactor(" ");


thrown.expect(MessageException.class); thrown.expect(MessageException.class);
thrown.expectMessage("\"foo\\bar\" is not a valid project or module key");
thrown.expectMessage("\" \" is not a valid project or module key");
underTest.validate(reactor); underTest.validate(reactor);
} }


}; };
} }


@Test
public void fail_with_only_digits() {
ProjectReactor reactor = createProjectReactor("12345");

thrown.expect(MessageException.class);
thrown.expectMessage("\"12345\" is not a valid project or module key");

underTest.validate(reactor);
}

@Test @Test
public void fail_when_branch_name_is_specified_but_branch_plugin_not_present() { public void fail_when_branch_name_is_specified_but_branch_plugin_not_present() {
ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo"); ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo");

正在加载...
取消
保存