The Official Blog for LifterLMS Contributors

  • LifterLMS Version 7.1.2

    Updates and Enhancements
    • Making the LifterLMS logo link to the site.
    Bug Fixes
    • Fix bug in llms_featured_img function when featured image file is not available. #2381
    • Fixed manual certificates awarding broken when using the block editor. #2386
    Read more
  • LifterLMS Advanced Videos Version 1.0.0-beta.20

    Updates and Enhancements
    • Replaced use of the deprecated FILTER_SANITIZE_STRING constant.
    Bug Fixes
    • Fixed possible race condition breaking the countdown when using the autoadvance feature.
    Performance Improvements
    • Improve performance by skip counting the total video events when querying the db for the last video event or whether the video was completed.
    Read more
  • LifterLMS Version 7.1.1

    Bug Fixes
    • Fixed notice display on WooCommerce dashboard pages.
    • Fixed View button URL when using WP in subdirectory.
    • Fixed blank System Report’s copy for Support.
    Read more
  • LifterLMS Version 7.1.0

    New Features
    • Added lessons count column on the Courses post list table.
    • Added a new Dashboard page under the LifterLMS menu in the admin, whicih includes recent activity widgets and links to useful resources.
    • Added link to the course builder for each lesson on the Lessons post list table. Also added a link to either edit or add a quiz.
    Updates and Enhancements
    • Updates LifterLMS Helper to v3.5.0.
    • Make the LifterLMS menu meta box initially available on Appearance -> Menus.
    • Updates LifterLMS REST to v1.0.0-beta.26.
    Bug Fixes
    • Catch possible fatal when trying to display a “broken” basic notification and set its status to ‘error’ so that it’ll be excluded from the next fetches.
    • Catch possible fatal when sending notification emails and in that case remove from the queue the item that produced it.
    • Fix cloned course retaining original course’s ID in some restriction messages.
    • Fixed possible admin notices duplication when activating/deactivating or installing add-ons from the page Add-ons & more.
    • Avoided setting the llms-tracking cookie when there are no events to track.
    • Updated styles across the entire plugin.
    • Updated Add-ons & more list to hide old (uncategorized) products.
    • Deprecated methods LLMS_Admin_Notices_Core::sidebar_support() and LLMS_Admin_Notices_Core::clear_sidebar_notice().
    • Removed notice for theme sidebar support.
    Developer Notes
    • The function llms_is_user_enrolled() will always return false for non existing users. While, before, it could return true if a now removed user was enrolled into a the given course or membership.
    • Added new LLMS_Course::get_lessons_count() method. It can be used in place of count( LLMS_Course::get_lessons() ) to improve performance.
    • Fixed compatibility with PHP 8.1 by using an empty string as menu parent page for the course builder submenu page in place of NULL.
    • Avoid passing null values to urlencode() and urldecode() that would produce PHP warnings on PHP 8.1+.
    • Added $autoload parameter to the function llms_get_student.
    Performance Improvements
    • Improve performance when querying notifications via the LLMSNotificationsQuery and there’s no need to count the total notifications found, or for pagination information.
    • Immediately return false when running llms_is_user_enrolled() on logged out or no longer existing users, avoiding running additional DB queries e.g. when displaying course or membership catalogs for visitors.
    • Skip counting the total transactions found when retrieving the last or the first transaction for an order.
    Updated Templates
    • templates/admin/reporting/nav-filters.php
    • templates/admin/reporting/reporting.php
    • templates/admin/reporting/tabs/courses/course.php
    • templates/admin/reporting/tabs/memberships/membership.php
    • templates/admin/reporting/tabs/quizzes/quiz.php
    • templates/admin/reporting/tabs/students/student.php
    • templates/admin/reporting/tabs/widgets.php
    • templates/checkout/form-confirm-payment.php
    Read more
  • LifterLMS Helper Version 3.5.0

    Updates and Enhancements
    • Updated the appearance of the license dropdown.
    Bug Fixes
    • Fixed incorrect HTML code for single Add On displayed on the LifterLMS > Add-ons & more screen.
    Developer Notes
    • Added new paramater $force, false by default, to the static method LLMS_Helper_Keys::activate_keys(). It’ll allow to force a remote call instead of using ccached results.
    Performance Improvements
    • Cache results from the activate keys calls to the LifterLMS license API. This prevents issues where sites are pinging the license server too often, specifically if they are “cloned” sites.
    Read more
  • LifterLMS REST API Version 1.0.0-beta.26

    Bug Fixes
    • Removed the extra parameter passed to LLMS_Student::enroll() during status updates. #278
    • Fixed an issue that produced the enrollment of the current user into a course when they were trying to enroll an user with ID 0. #308
    Developer Notes
    • The LifterLMS Core minimum required version has been raised to version 7.0.2. #308
    Read more
  • LifterLMS Labs Version 1.6.1

    • Fixed display issues on the Labs settings page.
    Read more
  • LifterLMS MailChimp Version 3.3.1

    Bug Fixes
    • Fixed Mailchimp subscription not working when groups were set with plugin version 3.3.0.
    Read more
  • LifterLMS MailChimp Version 3.3.0

    Updates and Enhancements
    • The LifterLMS Core minimum required version has been raised to version 5.9.0.
    • Replaced use of the deprecated FILTER_SANITIZE_STRING constant.
    • Can now set multiple groups to be added to when enrolled in a course.
    Bug Fixes
    • Better handling and logging of Mailchimp errors.
    • Fixed groups not available in the integration’s settings as soon as the integration was enabled and a list selected.
    Read more
  • LifterLMS Version 7.0.1

    Bug Fixes
    • Fixed a fatal error encountered on the payment confirmation screen when attempting to confirm a non-existent order. #2093
    • Use sanitize_file_name() in favor of sanitize_title() for generating the file name of reporting table export files. #1540
    • Resolved conflict encountered on post edit screens when using LifterLMS, Yoast SEO, and the Classic Editor plugin. #2298
    Developer Notes
    • A stub method, get_title() has been added to the LLMS_Abstract_Exportable_Admin_Table abstract class. This method should be defined by any extending classes and will throw a _doing_it_wrong() error when undefined.
    • Added new filter to allow customizing which user roles are affected by the LLMS_Admin_Menus::instructor_menu_hack function.
    Read more
  • Announcing the Public Launch of the LifterLMS Vulnerability Disclosure Program on the Bugcrowd Platform

    Since 2019, LifterLMS has maintained a Vulnerability Disclosure Program. Our program has evolved since it’s initial iteration and today we’ve opened our formerly private program to any researcher on the Bugcrowd platform.

    We’ve always taken care to ensure our software has best-in-class security but, as any software or company, we’ve grown, evolved, and learned.

    We’ve had failures and successes and in all things we’ve endeavored to build secure software so our users can focus on education and training first. We are not, nor do we pretend to be, an enterprise solution. But we do aim to ensure our software is as safe and secure as any enterprise alternative.

    While considering whether or not to announce the public launch of our program on Bugcrowd I started thinking back through my memory about how we arrived at where we are today. In thinking about it I decided to write something of a history of our team’s security journey.

    Our First Vulnerability Disclosure

    In the fall of 2019 an anonymous security researcher disclosed a vulnerability in the LifterLMS plugin to the Plugins Review Team. As a result, our plugin was de-listed from the Plugin Repository.

    My initial reaction to the email was shame.

    I reviewed the offending code in disbelief. How could I have written and shipped this code. It was so obviously flawed it should have never been released. I should have known better and I did know better. Yet, here it was. The facts of the vulnerability were indisputable.

    So, we fixed it. As we’ve found over the years, fixing a vulnerability is often quite simple. It’s trivial to see an exploit once you’re alerted to it. A few hours after we were de-listed, the issue was resolved and the next morning the plugin was re-listed.

    Before this incident we thought about proactive security. Our coding process requires code review from another developer before any code is published. We talked about security. We kept ourselves apprised of best practices and common exploits.

    We ran automated tests and performed static analysis against all our code. We knew this wasn’t enough but we didn’t know what was better.

    This vulnerability demonstrated, publicly, that our intentions and processes were limited and fallible. Human error and oversight could be mitigated but not entirely prevented.

    Launching a Self-Managed Bug Bounty Program

    After resolving the issue my initial shame and embarrassment had time to fester. Instead of feeling confident that we’d fixed a problem, I felt terrified that there were more issues and oversights. A multi-developer audit of our codebases resulted in no additional vulnerabilities. And instead of feeling safe, I was haunted.

    We don’t know what we don’t know.

    Chris and I flew to Pressnomics 6 in Tuscon a few weeks later. One of the talks was given by a security engineer at Pagely. After his presentation he was kind enough to act as a security therapist. He listened to my story and nodded with empathy.

    He said “It will happen again” and “It will be okay.”

    He said “As long as researchers know how to find you, they will.”

    He said “Make it stupid easy for them contact you.”

    When I got home I contacted HackerOne and Bugcrowd and learned that it’s not terribly affordable to run a vulnerability disclosure program on these platforms. After weeks of conversations with both parties we decided that maybe there’s a reason why the only WordPress plugins I could find with bug bounties and security programs had 500,000+ active installs. At the time we had less than 10,000.

    So we launched our own security program and bug bounty. We published the first version of our security program at The page outlined our security disclosure and research policy. It included a relatively low-paying bounty schedule.

    We paid out a few bounties over the next six months. The program was not a success but we decided it was better than nothing. If a researcher found something, they’d be able to get it to us safely. Our primary goal was to prevent any future de-listing by ensuring security researchers knew how to contact us should they discover any vulnerabilities in the future.

    This superficial goal arose out of the implications of a statement the plugin review team made to us in their relisting email:

    “…once a plugin is closed, many people will think it is because it was insecure, even if it wasn’t. That means your plugin becomes a target for hackers.

    In other words, de-listing due to a security vulnerability alerts malicious actors to the presence of an unpatched vulnerability.

    Ensuring researchers could communicate us, in favor of the plugins review team, meant we could fix vulnerabilities without being de-listed and in doing so we could reduce the number of people, specifically malicious actors or hackers, who were aware of an unpatched vulnerability.

    Triage is Difficult and Time Consuming or The Next Human Oversight

    In the late spring of 2020 our program was discovered by a group of student security researchers. They shared and posted our policy to various Facebook groups and forums.

    Over the course three weeks, we triaged nearly 200 vulnerability reports, almost all were invalid or informational reports related to things like security headers on (not the LifterLMS codebase). Often these emails were hostile and contained minimal useful information. The researchers insisted that we pay them for their efforts even if we found their reports invalid, requested more information, or were duplicate reports.

    I did my best to arrive at a mutually beneficial agreement with this group. In the moment, while overwhelmed, and not truly understanding what was happening, I mistakenly assumed that it was a small group of friends or acquaintances. I tried to hire them as a team and pay them a monthly stipend for security research.

    However, while trying to discuss agreeable terms with a person who identified themselves as a “leader” of the group, reports continued to come in. It became clear that the problem was our own making.

    I made an enormous error in drafting our security program: I had no idea how to effectively communicate with security researchers and I didn’t understand how to write a research brief with a meaningful scope.

    We decided to cease communications, delete many of these emails, and suspend the self-managed program.

    Launching and Maintaining a Manged Vulnerability Disclosure Program

    So I returned phone calls to Bugcrowd and in July we reopened our security program, this time with managed triage on the Bugcrowd platform.

    We launched the program as a private, invitation-only program and over the past two years invited more than 300 Bugcrowd researchers to test LifterLMS, our websites, and codebases.

    Today there are 66 individual researchers who have joined our program. We’ve received 111 total submissions and accepted (and fixed) 20.

    Submission OutcomeCount

    Of the accepted submissions, 2 were high severity, 7 were medium severity, and 10 were low severity. The remaining submissions were informational.

    Submission technical severity graph

    Growing our Program and Improving the Security and Stability of LifterLMS

    We consider our partnership with Bugcrowd to be a success. In partnering, we’ve managed to remove the bulk of triage effort from our developers, the Bugcrowd Application Security Engineers intake reports and let us know when the report has been validated or when they require our assistance to validate.

    We’ve successfully patched two high-severity vulnerabilities before they were disclosed publicly. On one hand, it’s terrible that we’ve had any high-severity vulnerabilities. But patching them following responsible, private disclosure is something to celebrate. To our knowledge these issues were never publicly exploited.

    This is the ultimate goal of security research. To improve the security of our software by leveraging the knowledge and experience of security experts.

    Together, with our partners at Bugcrowd, we’ve determined that the path towards further growth and improvement is to open our program to any interested researcher.

    Read more
  • LifterLMS Stripe Payment Gateway Version 5.5.0

    Updates and Enhancements
    • Upgrades the Stripe API version to 2022-08-01. These breaking changes do not affect this payment integration.
    • Raised the minimum supported WP version from 5.3 to 5.6.
    Read more