Skip to content

Commit 554ffab

Browse files
committed
Security: Enforce contextual validation of resourceLinkList in CreateCBlogAction to prevent bypassing course, session, or group access gates.
1 parent d08136c commit 554ffab

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

src/CoreBundle/Controller/Api/CreateCBlogAction.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Bundle\SecurityBundle\Security;
1717
use Symfony\Component\HttpFoundation\Request;
1818
use Symfony\Component\HttpKernel\Attribute\AsController;
19+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
1920

2021
#[AsController]
2122
class CreateCBlogAction extends BaseResourceFileAction
@@ -42,6 +43,12 @@ public function __invoke(
4243
$resourceLinkList = \is_array($resourceLinkListRaw) ? $resourceLinkListRaw : [];
4344
}
4445

46+
// The `cid` (and optional `sid`/`gid`) query parameter establishes the
47+
// course context that gated the security expression. Any
48+
// resourceLinkList entry that points to a different context would
49+
// bypass that gate, so we reject the request outright.
50+
$this->assertResourceLinkListMatchesQueryContext($request, $resourceLinkList, $security);
51+
4552
$blog = (new CBlog())
4653
->setTitle($title)
4754
->setBlogSubtitle($subtitle)
@@ -89,4 +96,47 @@ private function handleShortcutCreation(
8996
$shortcutRepository->addShortCut($blog, $currentUser, $course, $session);
9097
}
9198
}
99+
100+
/**
101+
* @param array<int, mixed> $resourceLinkList
102+
*/
103+
private function assertResourceLinkListMatchesQueryContext(
104+
Request $request,
105+
array $resourceLinkList,
106+
Security $security,
107+
): void {
108+
if ($security->isGranted('ROLE_ADMIN')) {
109+
return;
110+
}
111+
112+
if ([] === $resourceLinkList) {
113+
return;
114+
}
115+
116+
$queryCid = (int) $request->query->get('cid');
117+
$querySid = (int) $request->query->get('sid');
118+
$queryGid = (int) $request->query->get('gid');
119+
120+
foreach ($resourceLinkList as $entry) {
121+
if (!\is_array($entry)) {
122+
continue;
123+
}
124+
125+
$entryCid = (int) ($entry['cid'] ?? 0);
126+
$entrySid = (int) ($entry['sid'] ?? 0);
127+
$entryGid = (int) ($entry['gid'] ?? 0);
128+
129+
if ($entryCid > 0 && $entryCid !== $queryCid) {
130+
throw new AccessDeniedHttpException('resourceLinkList course does not match the request context.');
131+
}
132+
133+
if ($entrySid > 0 && $entrySid !== $querySid) {
134+
throw new AccessDeniedHttpException('resourceLinkList session does not match the request context.');
135+
}
136+
137+
if ($entryGid > 0 && $entryGid !== $queryGid) {
138+
throw new AccessDeniedHttpException('resourceLinkList group does not match the request context.');
139+
}
140+
}
141+
}
92142
}

src/CourseBundle/Entity/CBlog.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
#[ORM\Table(name: 'c_blog')]
3838
#[ApiResource(
3939
operations: [
40-
new GetCollection(security: "is_granted('ROLE_USER')"),
40+
new GetCollection(
41+
security: "is_granted('ROLE_CURRENT_COURSE_STUDENT') or is_granted('ROLE_CURRENT_COURSE_SESSION_STUDENT')",
42+
),
4143
new Post(
4244
controller: CreateCBlogAction::class,
4345
openapi: new Operation(
@@ -65,7 +67,7 @@
6567
]),
6668
),
6769
),
68-
security: "is_granted('ROLE_CURRENT_COURSE_TEACHER') or is_granted('ROLE_CURRENT_COURSE_SESSION_TEACHER') or is_granted('ROLE_TEACHER')",
70+
security: "is_granted('ROLE_CURRENT_COURSE_TEACHER') or is_granted('ROLE_CURRENT_COURSE_SESSION_TEACHER')",
6971
validationContext: ['groups' => ['Default', 'blog:write']],
7072
deserialize: false
7173
),

0 commit comments

Comments
 (0)