MDL-79041 core: Better handling of the MoodleNet resource URL

- Switch use PARAM_TEXT instead of PARAM_URL for resource URL
 - Added noreferrer to the Go to MoodleNet drafts button, to avoid the risks associated with opening in
   _blank without removing access to the referrer and opener
This commit is contained in:
Huong Nguyen
2023-09-28 11:32:59 +07:00
parent c6e727732a
commit 1dda6c86b8
5 changed files with 123 additions and 3 deletions
+4 -1
View File
@@ -153,7 +153,10 @@ class moodlenet_send_activity extends external_api {
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'status' => new external_value(PARAM_BOOL, 'Status: true if success'),
'resourceurl' => new external_value(PARAM_URL, 'Resource URL from MoodleNet'),
// We used PARAM_TEXT instead of PARAM_URL because the URL return from MoodleNet may contain some characters.
// It does not match with PARAM_URL, but the URL still works.
// Since we just show the response resource URL to the user for them to navigate to MoodleNet, it would be safe.
'resourceurl' => new external_value(PARAM_TEXT, 'Resource URL from MoodleNet'),
'warnings' => new external_warnings(),
]);
}
+4 -1
View File
@@ -212,7 +212,10 @@ class moodlenet_send_course extends external_api {
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'status' => new external_value(PARAM_BOOL, 'Status: true if success'),
'resourceurl' => new external_value(PARAM_URL, 'Resource URL from MoodleNet'),
// We used PARAM_TEXT instead of PARAM_URL because the URL return from MoodleNet may contain some characters.
// It does not match with PARAM_URL, but the URL still works.
// Since we just show the response resource URL to the user for them to navigate to MoodleNet, it would be safe.
'resourceurl' => new external_value(PARAM_TEXT, 'Resource URL from MoodleNet'),
'warnings' => new external_warnings(),
]);
}
@@ -20,7 +20,7 @@
}
}}
<div class="moodlenet-action-buttons">
<a class="btn btn-primary" href="{{resourseurl}}" target="_blank">
<a class="btn btn-primary" href="{{resourseurl}}" target="_blank" rel="noopener noreferrer">
{{#str}} moodlenet:gotomoodlenet, moodle {{/str}}
</a>
</div>
+57
View File
@@ -111,4 +111,61 @@ class moodlenet_send_activity_test extends externallib_advanced_testcase {
$this->assertEquals($issuer->get('id'), $result['warnings'][0]['item']);
$this->assertEquals(get_string('moodlenet:issuerisnotauthorized', 'moodle'), $result['warnings'][0]['message']);
}
/**
* Test execute_returns() method.
*
* @dataProvider return_resource_url_provider
* @covers ::execute_returns
*/
public function test_moodlenet_send_activity_return_resource_url(bool $state, string $resourceurl) {
$this->resetAfterTest();
// Create dummy result with the resourceurl.
$result = [
'status' => true,
'resourceurl' => $resourceurl,
'warnings' => [],
];
if (!$state) {
$this->expectException(\invalid_response_exception::class);
}
$result = external_api::clean_returnvalue(moodlenet_send_activity::execute_returns(), $result);
if ($state) {
$this->assertEquals($resourceurl, $result['resourceurl']);
}
}
/**
* Provider for test_moodlenet_send_activity_return_resource_url().
*
* @return array Test data.
*/
public function return_resource_url_provider(): array {
return [
'Success 1' => [
true,
'https://moodlenet.example.com/drafts/view/testactivity_backup.mbz',
],
'Success 2' => [
true,
'https://moodlenet.example.com/drafts/view/testactivity_backup with spaces.mbz',
],
'Success 3' => [
true,
'https://moodlenet.example.com/drafts/view/testactivity_backup with " character.mbz',
],
'Success 4' => [
true,
"https://moodlenet.example.com/drafts/view/testactivity_backup with ' character.mbz",
],
'Success 5' => [
true,
'https://moodlenet.example.com/drafts/view/testactivity_backup with < and > characters.mbz',
],
'Fail 1' => [
false,
'https://moodlenet.example.com/drafts/view/testactivity_backupwith<lang lang="en">a<a</lang>html.mbz',
],
];
}
}
+57
View File
@@ -117,4 +117,61 @@ class moodlenet_send_course_test extends externallib_advanced_testcase {
$this->assertEquals($issuer->get('id'), $result['warnings'][0]['item']);
$this->assertEquals(get_string('moodlenet:issuerisnotauthorized', 'moodle'), $result['warnings'][0]['message']);
}
/**
* Test execute_returns() method.
*
* @dataProvider return_resource_url_provider
* @covers ::execute_returns
*/
public function test_moodlenet_send_course_return_resource_url(bool $state, string $resourceurl) {
$this->resetAfterTest();
// Create dummy result with the resourceurl.
$result = [
'status' => true,
'resourceurl' => $resourceurl,
'warnings' => [],
];
if (!$state) {
$this->expectException(\invalid_response_exception::class);
}
$result = external_api::clean_returnvalue(moodlenet_send_course::execute_returns(), $result);
if ($state) {
$this->assertEquals($resourceurl, $result['resourceurl']);
}
}
/**
* Provider for test_moodlenet_send_course_return_resource_url().
*
* @return array Test data.
*/
public function return_resource_url_provider(): array {
return [
'Success 1' => [
true,
'https://moodlenet.example.com/drafts/view/testcourse_backup.mbz',
],
'Success 2' => [
true,
'https://moodlenet.example.com/drafts/view/testcourse_backup with spaces.mbz',
],
'Success 3' => [
true,
'https://moodlenet.example.com/drafts/view/testcourse_backup with " character.mbz',
],
'Success 4' => [
true,
"https://moodlenet.example.com/drafts/view/testcourse_backup with ' character.mbz",
],
'Success 5' => [
true,
'https://moodlenet.example.com/drafts/view/testcourse_backup with < and > characters.mbz',
],
'Fail 1' => [
false,
'https://moodlenet.example.com/drafts/view/testcourse_backupwith<lang lang="en">a<a</lang>html.mbz',
],
];
}
}