Skip to content

Ccx notes - #39105

Draft
scottrish wants to merge 58 commits into
openedx:masterfrom
scottrish:ccx_notes
Draft

Ccx notes#39105
scottrish wants to merge 58 commits into
openedx:masterfrom
scottrish:ccx_notes

Conversation

@scottrish

Copy link
Copy Markdown

Description

Describe what this pull request changes, and why. Include implications for people using this change.
Design decisions and their rationales should be documented in the repo (docstring / ADR), per
OEP-19, and can be
linked here.

Useful information to include:

  • Which edX user roles will this change impact? Common user roles are "Learner", "Course Author",
    "Developer", and "Operator".
  • Include screenshots for changes to the UI (ideally, both "before" and "after" screenshots, if applicable).
  • Provide links to the description of corresponding configuration changes. Remember to correctly annotate these
    changes.

Supporting information

Link to other information about the change, such as Jira issues, GitHub issues, or Discourse discussions.
Be sure to check they are publicly readable, or if not, repeat the information here.

Testing instructions

Please provide detailed step-by-step instructions for testing this change.

Deadline

"None" if there's no rush, or provide a specific date or event (and reason) if there is one.

Other information

Include anything else that will help reviewers and consumers understand the change.

  • Does this change depend on other changes elsewhere?
  • Any special concerns or limitations? For example: deprecations, migrations, security, or accessibility.
  • If your database migration can't be rolled back easily.

farhaanbukhsh and others added 30 commits April 30, 2026 20:52
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
This update includes the new editor design.  This is a backport of this
new editor to the verawood release branch.
…dx#38477) (openedx#38495)

Backport of openedx#38477 / cherry picked from commit 8bcdd46

Co-authored-by: Copilot <copilot@github.com>
- Integrate extended profile model into account settings flow
- Improve validation and error handling
- Refactor form handling and API separation
- Ensure atomic updates for profile changes
- Support PROFILE_EXTENSION_FORM setting
- Add and update comprehensive unit tests
- Improve documentation and code clarity
feat: add support for course permission in authz rest apis

Commit generated by workflow `openedx/openedx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`

Co-authored-by: MaferMazu <35668326+MaferMazu@users.noreply.github.com>
Signed-off-by: Farhaan Bukhsh <farhaan@opencraft.com>
This addresses a bug that was breaking course export when edx-val based
transcripts were missing from the file store. With this fix, the course
export skips the missing transcript without erroring out entirely.
Maps the legacy STUDIO_BASE_URL key to the optional frontend-base
cmsBaseUrl field so it surfaces at the top level of FrontendSiteConfigView
instead of falling through to commonAppConfig.

Co-Authored-By: Claude <noreply@anthropic.com>
Adds a structural translator from the legacy PARAGON_THEME_URLS shape
(frontend-platform's env.config.jsx) into the frontend-base SiteConfig
`theme` setting served by FrontendSiteConfigView.

frontend-base loads Paragon's base CSS via its shell stylesheet, so the
runtime `theme` is exclusively a brand-override mechanism.  The
translator deliberately narrows to `urls.brandOverride` only; bare `url`
and `urls.default` would point at Paragon defaults and clobber the
bundled CSS, so they are dropped.  `defaults` passes through unchanged
when at least one URL survives, and is dropped (along with the whole
`theme` key) when no URL survives, since `defaults` alone is meaningless
without a stylesheet to point at.

PARAGON_THEME_URLS is also added to the site-level keys stripped from
per-app MFE_CONFIG_OVERRIDES, since `theme` is site-level only in
frontend-base.

Co-Authored-By: Claude <noreply@anthropic.com>
Purposefully not adding the older releases here but for any new releases
we should test on the branch once PRs are merged to make sure there are
no regressions that were missed by weird merge timing.
…oints (openedx#38458)

- Support ?ordering= query param on special exam allowances and
  attempts list endpoints via in-memory sort (edx-proctoring returns
  plain lists, not querysets; see openedx/edx-proctoring#1320)
- Add derived exam_type field (timed/proctored/practice) to special
  exam and attempt serializer responses
- Return authenticated user's username in course metadata response
- Make reason field optional and blank-safe on due date extension
  endpoint (BlockDueDateSerializerV2)
* feat: add generate exception certificates modal

* feat: add tests

* fix: tests

* fix: linting
The set_course_mode_price view had no authorization check beyond
@login_required, meaning any authenticated user could POST to it and
rewrite the honor-mode price for any course — a privilege escalation
vulnerability.

Ideally this endpoint would be removed: it has no known callers in the
UI (no templates or JS reference it), no tests, and targets the legacy
'honor' mode. However, it is publicly routed and external consumers may
depend on it, so removal requires going through the DEPR process before
we can act. In the meantime, this commit closes the security hole
regardless of how active the endpoint is.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…x#38578) (openedx#38675)

FieldError at /admin/content_libraries/contentlibrary/:id/change/
Unknown field(s) (authorized_lti_configs)

Co-authored-by: Braden MacDonald <braden@opencraft.com>
validate_timestamp_and_nonce previously returned True unconditionally,
allowing any captured LTI launch request to be replayed indefinitely.

Now rejects requests whose oauth_timestamp falls outside a ±5-minute
window, then atomically records the nonce in the Django cache via
cache.add() (OEP-0022 key generation via get_cache_key). A replay
returns False immediately because cache.add() only writes when the key
is absent.

TieredCache is intentionally not used here: it has no atomic add
primitive, so a separate get-then-set would leave a race window that
defeats the protection. See the updated docstring for details.

Documents the requirement for a shared cache backend (Redis or
Memcached) in multi-node deployments in both the app and repo READMEs.

Fixes GHSA-6gm5-c49g-p3h9

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add missing class docstring to TimestampAndNonceValidatorTest (C0115)
- Move time.time patch from class decorator into setUp/addCleanup to
  eliminate unused mock parameters in every test method (PT019)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
)

* fix: ValidationError when trying to add components to a collection (openedx#38579)

* fix: don't sleep for a half second every time we update meilisearch (openedx#38576)

* fix: various issues with modulestore migrator and content library events (openedx#38508)

* fix: update modulestore migrator to not publish in draft context
* fix: use correct date/user info for modulestore migrator
* fix: allow our event handling tasks to call subtasks
* fix: better handling of deletion/un-deletion using openedx-core fixes
* fix: exception raised when mass-deleting all items in a content library
  (ContentLibraryBlockNotFound was being raised and not caught, propagating up to celery task error)
* fix: don't update the 'collections' field of deleted entities when modifying a collection
* fix: keep collection "# of entities" count updated when entities deleted
* refactor: use LIBRARY_COLLECTION_UPDATED event to update collections on entity (un)delete
* fix: unable to delete "ghost" component in library if it exists but has no draft

* fix: error when deleting lib component used in a container (openedx#38607)

* fix: bump openedx-core to v1.0.2

---------

Co-authored-by: Claude <noreply@anthropic.com>
BryanttV and others added 24 commits June 25, 2026 12:41
…edx#38788)

* feat: add platform glob scope support (openedx#38660)

* feat: add platform-wide authz scope support

* feat: enhance course listing authorization with global toggle support

* chore: upgrade openedx-authz to 1.19.0

* docs: add docstring for mock authorization toggle in course listing tests

* fix: add missing org scope support in instructor dashboard (openedx#38721)

* feat: enhance role assignment handling for users with org-wide scopes

* refactor: update role assertion methods

* refactor: replace external_key initialization with build_external_key method

* refactor: streamline role assignment by directly using build_external_key method

* refactor: update role assignment scope initialization to use ScopeData

* refactor: introduce helper function to extract org and course ID from AuthZ scope

* refactor: replace has_access with administrative_accesses_to_course_for_user

* docs: clarify legacy-only CourseAccessRole query in studio course list

* refactor: simplify user role assignment retrieval by using scoped api method

* fix: preserve catalog and staff checks for authZ about-page access (openedx#38736)

* fix: preserve catalog and staff checks for authZ about-page access

* refactor: remove about page catalog visibility error function and return CatalogVisibilityError directly
* feat: added ora reminder notification
…t-ora-reminder-verawood

[Backport] feat: add ora reminder notification
…openedx#38852)

Replace course_version in the sidebar cache key with a
block_structure_version derived from BlockStructureModel.data_version.
course_version changes eagerly on publish, causing a cache miss before
the block structure is rebuilt — poisoning the cache with stale data for
1 hour. block_structure_version only changes after the async rebuild
completes, so cache misses only occur when fresh data is available.

This is a backport of openedx#38785.
The previous vendored pdf.js was 1.0.907 (May 2013), four major versions
behind upstream and within the range covered by Mozilla's
GHSA-wgrm-67xf-hhpq (arbitrary JavaScript execution upon opening a
malicious PDF). 5.7.284 is well past the >= 4.2.67 fix line.

The replacement comes from Mozilla's prebuilt
`pdfjs-5.7.284-legacy-dist.zip` GitHub Release artifact rather than the
`pdfjs-dist` npm package because the npm package is library-only -- it
ships `pdf.mjs` plus a bare `PDFViewer` component class, but no
`viewer.html` / `viewer.mjs` / `viewer.css` / locale files. A full npm
integration would mean rewriting the viewer page against the bare
component, which is appropriate as a non-security follow-up but not as
the fix here.

The viewer page (`lms/templates/pdf_viewer.html`) is rewritten as a Mako
adaptation of upstream `web/viewer.html`. A `<base href>` makes the
viewer's relative asset URLs resolve against the vendored copy.

The analytics shim (`lms/static/js/pdf-analytics.js`) is rewritten in
vanilla JS against `PDFViewerApplication.eventBus`. Four analytics
events (`textbook.pdf.thumbnails.toggled`,
`textbook.pdf.thumbnail.navigated`, `textbook.pdf.outline.toggled`,
`textbook.pdf.page.scrolled`) no longer fire because the corresponding
UI elements were refactored away in pdf.js 4.x's Views Manager
redesign.

A new `scripts/refresh-pdfjs-vendor.sh` is the tool for future bumps:
update PDFJS_VERSION + PDFJS_LEGACY_ZIP_SHA256, re-run, commit.

Closes GHSA-mj74-gfq3-2v9f.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ready exist (openedx#38840) (openedx#38858)

* fix: Cannot rerun courses - authz role assignment expects rerun to already exist

* squash!: Increase test coverage

* squash!: Improve tests

* squash!: Add TODO comments and improve assert handling
chore: upgrade to the latest openedx-authz version

Commit generated by workflow `openedx/openedx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`

Co-authored-by: Maria Grimaldi (Majo) <maria.grimaldi@edunext.co>
This PR enables the PDF block by default in all courses. It is
listed under the the Advanced component category.

Before this commit, course authors needed to add it to the
Advanced module list in Advanced Settings first.

https://openedx.atlassian.net/wiki/spaces/OEPM/pages/5335908397

Backports f4cba03
…8866)

* feat: scope immediate notification email batching per user

Immediate-email batching was scoped by user AND course_id, so a learner
enrolled in multiple active courses received a separate buffered digest
per course. Notification preferences are now account-level, so batching
should feel account-level from the learner's perspective too.

Scope the buffer by user only in decide_email_action,
schedule_digest_buffer and send_buffered_digest: the first eligible
notification is sent immediately, and additional immediate notifications
within NOTIFICATION_IMMEDIATE_EMAIL_BUFFER_MINUTES are grouped into a
single digest regardless of which course generated them. The digest
already resolves per-notification course names, so multi-course digests
render unchanged.

course_key is retained in send_buffered_digest's signature so digest
tasks enqueued before the change still deserialize.

Closes openedx/tutor-contrib-platform-notifications#66

* fix: anchor buffered digest to last immediate email so wait stays buffer_minutes

The digest was scheduled buffer_minutes from when the SECOND notification was
processed, so the total wait after the immediate email was (gap to 2nd
notification) + buffer_minutes -- observed as ~22 min for a 15 min buffer.

Anchor scheduled_time to last_sent.email_sent_on + buffer_minutes instead, and
clamp to now() when that window has already elapsed. Add tests for both the
relative-to-last-email schedule and the already-elapsed fast path.
Course -> Library import would previously fail if the block_id contained
a period, but periods are legal in UsageKeys and can turn up, e.g.
"good-vs.-evil" should be allowed. This is not *common* because most
courses are edited in Studio and don't get descriptive block_ids. But
OLX-edited courses can end up with block_ids like this.

Backports: 04fc6e4
Fixes: openedx#38609
…-verawood

feat: scope immediate notification email batching per user (backport openedx#38866)
These bring in library fixes around importing content with blank titles,
restoring entities from archives where the keys do not match the file
names, as well as a small tweak to reduce the size of backup archives.
…lag (openedx#38855) (openedx#38891)

Granting and exercising course creator access crashed once
authz.enable_course_authoring was on. The courses.create_course
permission was implemented in AuthZ, but the course creator role itself
was never migrated there. The code deciding whether to use the AuthZ path
only checked the flag's state, not whether the specific role had actually
been migrated, so unmigrated roles like course creator still used the
AuthZ path and crashed.

enable_authz_course_authoring now takes an optional role and falls back
to the course creator role's own check for any role without a migrated
AuthZ equivalent, regardless of the flag. See ADR 0027.

Merged to master as openedx/openedx-platform@0f57cf2bbf1.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…nedx#38893)

Studio modal doesn't allow authors to scroll when content is longer than the screen height, so this commit adds a max-height based on screen height.

(cherry picked from commit c82accb)
…email (openedx#38887)

When a third-party auth provider has "skip registration form" enabled, the
authn MFE context set autoSubmitRegForm=True unconditionally. Providers such
as Facebook (email permission denied) or Microsoft Entra ID can complete
authentication without returning an email claim; auto-submitting the
registration form in that case silently fails client-side validation and
leaves the learner stuck on a partially-filled form with no explanation.

Gate the auto-submit on the provider actually returning an email so the
learner falls back to the normal registration form and can fill in what is
missing.

Fixes: openedx#38780
Discussion thread titles (and other user-controlled context fields —
replier_name, author_name, username) were interpolated raw into
notification.content via `str.format(**context)`. That output is
rendered with Django's `|safe` filter in digest_content.html, which
is included by both the email_digest and batched_email body
templates, so a `<style>` block in a thread title survived into
recipient inboxes as executable CSS on email open — enabling
open-tracking, content spoofing, and phishing.

Escape at the source: in `get_notification_content`, wrap every
context value with `django.utils.html.escape` before
`template.format(**context)`, exempting the two structural keys
(`p`, `strong`) that content_templates use as HTML tag names. This
defends every renderer of `notification.content` in one place.

This is the incomplete-patch companion of GHSA-4xv3-5j4x-q8g4
(CVE-2026-42857), which sanitized the post body via
`clean_thread_html_body()` but did not cover the title path.
Fixes GHSA-rv5w-f4r5-h77g.

(cherry picked from commit 08b719ce41bb369fa0cabbe8d0547124e64c8566)
`startswith` is the wrong primitive for "is target inside directory base":
once a trailing separator drops anywhere along the way, sibling directories
whose names extend base match. We could spot-fix by re-appending the
separator before the check, but `commonpath` makes the directory-boundary
intent explicit and removes the failure mode entirely.

Fixes GHSA-6cmm-8875-5pcw.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
openedx#38821) (openedx#38844)

* fix: ComponentLinks were not deleted when parent container was deleted

* test: add a relevant test case and flag overlapping handlers

AI note: AI used to help write tests only.

Co-authored-by: Claude <noreply@anthropic.com>
Update edx-submissions to bring in CSRF changes to the LMS's new
xqueue interface.
…zer (openedx#38899)

This was in the legacy studio but is missing from the Studio MFE
CourseTeamManageAPIView at
lms/djangoapps/support/rest_api/v1/views.py declared only
`permission_classes = (IsAuthenticated,)`, so any authenticated
user — including a plain enrolled learner — could invoke GET and
use the 404-vs-200 response for user enumeration against arbitrary
email/username/user_id values, confirming which accounts exist and
are active. Callers holding any course/org `instructor` role
additionally received the target user's `staff`/`instructor` role
map across the caller's scope.

Gate `get()` on `_caller_can_manage_course_team()`, a new helper
that returns True only for:

  - is_staff (GlobalStaff), or
  - is_superuser, or
  - SupportStaffRole, or
  - any user with a CourseAccessRole of role="instructor"

This is the union of PUT's current authorization set (admin/staff/
superuser/instructor) and the global SupportStaffRole. Read access
must match write access — a user who can PUT role changes must be
able to see the current state before making them — and the endpoint
lives in the support module, so support staff belong in the set
too. Also extend `get_accessible_courses_for_user` so that
SupportStaffRole users get the same all-courses view as admin/
staff, otherwise they'd fall through to the instructor branch and
receive an empty result set.

Anonymous callers still get 401 from DRF's IsAuthenticated; plain
learners now get 403 before the target lookup runs, so the 404-vs-
200 enumeration discrepancy (CWE-204) is no longer reachable by
unauthorized callers. Course instructors retain their scoped view.

`put()` retains its existing authorization for now; it lacks
SupportStaffRole in the set — a separate design gap best handled in
a public follow-up after publication (cross-linked to
GHSA-95xv-3c54-c3pw), where the `_caller_can_manage_course_team`
helper can also become the shared PUT check.

Fixes GHSA-95xv-3c54-c3pw. Credit: 5ud0 / Tarmo Technologies.
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @scottrish!

This repository is currently maintained by @openedx/wg-maintenance-openedx-platform-oncall.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Submit a signed contributor agreement (CLA)

⚠️ We ask all contributors to the Open edX project to submit a signed contributor agreement or indicate their institutional affiliation.
Please see the CONTRIBUTING file for more information.

If you've signed an agreement in the past, you may need to re-sign.
See The New Home of the Open edX Codebase for details.

Once you've signed the CLA, please allow 1 business day for it to be processed.
After this time, you can re-run the CLA check by adding a comment below that you have signed it.
If the CLA check continues to fail, you can tag the @openedx/cla-problems team in a comment for further assistance.

🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

🔘 Update the status of your PR

Your PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate.


Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Sep 15, 2026
@github-project-automation github-project-automation Bot moved this to Needs Triage in Contributions Sep 15, 2026
@mphilbrick211

Copy link
Copy Markdown

Hi @scottrish! Let me know if you have any questions regarding submitting a CLA form. Also, when you have a moment, please add a description of your changes to the top of this pull request. Thanks!

@mphilbrick211 mphilbrick211 moved this from Needs Triage to Needs Tests Run or CLA Signed in Contributions Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

open-source-contribution PR author is not from Axim or 2U

Projects

Status: Needs Tests Run or CLA Signed

Development

Successfully merging this pull request may close these issues.