- Set the minimum PHP version to 7.2.
- Upgrade Halite to version 4.
- Rename Blueprints to Models.
- Rename Lenses to Views.
- Rename Landings to Controllers.
- Use Psalm to integrate static analysis with Travis CI. This gives us a greater level of confidence over type safety and bug avoidance.
- Cryptographically associate account recovery tokens with the row ID of the user who requested the reset. This means that updating the userid column of an existing recovery token will not allow you to login as the arbitrary user.
- #52, #137, #140: Allow users, groups, blog post categories, author profiles, and series to be deleted.
- #72: You can now configure how emails are sent out. All of the options currently provided by Zend\Mail.
- #128: All CMS Airship cookies send a Same-Site header (strictly). This adds another layer of resilience against CSRF attacks.
- #147: Implemented a framework for importing data (i.e. password hashes) into a CMS Airship project. This will allow users to log in with their old password, when Airship only knows the old password hash.
- #138, #141: In addition to being able to change the name of a blog category or author, you can also update the slug (and optionally create a redirect from the old slug to the new one).
- #148: You can now override the footer text.
- #149: Implemented a View History feature for Blog Posts.
- #155: You can now create user accounts from the Bridge.
- Update Gregwar/RST to v1.0.3 to prevent LFI attacks. HackerOne Report.
- #134: Fixed a few bugs that caused the installer to fail in weird ways during a fresh install (i.e. for Docker users).
- #136: If you don't specify a subheader in the blog config, nothing will be displayed.
- #139: If an author's biography is empty, the "About the Author" section will not be displayed. In a future version, we may change this behavior to be dependent on the status of a checkbox rather than the non-emptiness of the biography field.
- #142: Hide "Uncategorized" from the right menu if there are no blog posts without a category.
- #143: Fixed issues with date/time handling that broke post editing.
- #144:
Fixed the regular expression in the
required
attribute that caused browsers to prevent form submission. - #145: The "default format" is now respected by the forms that support different input formats.
- #146: Created a button to purge the caches.
- Hid the link to view blog post history, as that feature was overlooked. We'll implement it in version 1.4.0.
- Some image types can be viewed directly instead of always forcing a download. The enforcement logic is a whitelist (that gadgets can extend).
- Significant UI/UX improvements.
- Redesigned the Bridge UI to be more suitable for a control panel.
- The left menu in the Bridge is now collapsable, but automatically opens the sections which indicate your current location in the cabin.
- Update Halite to 2.2.0.
- Added a
WhiteList
filter, which is a strict typed alternative to switch-case whitelisting. - #129: Extension developers can now make their motifs configurable by end users.
- #114: We no longer display the database password on the databases page. This has always only been accessible to administrators, but now it is write-only from the web interface.
- #131: If an exception is thrown by the part of code that loads the logger, and the database driver was selected, it will no longer silently produce a white screen.
- #132: You can now control the date/time a blog post is published.
- #133: Added the "slug" field to the "Create New Blog Post" form.
- In addition to expiring after a set period of time, account recovery URLs can only be used once. This fixes this feature by making it in line with the expected behavior.
- Bootstrap (JS/CSS framework) was removed, as we don't use it.
- Dependency update (e.g. HTMLPurifier 4.8.0).
- Added logic to the Airship updater to attempt to run
composer install
(if we can) if an update includes acomposer.lock
file.
- Update Guzzle for upstream fix to CVE-2016-5385.
- Separate MIME type output filter into its own method, so it can be covered by unit tests.
- #115: More readable extension log.
- Disable MIME sniffing everywhere.
- Add several headers to static file downloads to prevent stored XSS vulnerabilities in Internet Explorer and Flash. HackerOne Report.
- The tool created in
1.2.2
for automated deployments was not functional before Airship was first installed.
- Improved Continuum/Keyggdrasil logging.
- Created a tool for automating step one of the installer from the command line.
- #111: Fixed bugs with the notary API that was breaking the auto-updater.
- Corrected the variable names used to handle uncaught exceptions.
- #46: You can now specify meta tags for blog posts.
- #57:
Our
Database
class can now connect to UNIX sockets. Set the hostname tounix:/path/to/socket
. - #65: You can now add database connections to the local database connection pool. You can also add database connection pools for app-specific purposes. In the future, we'll support more than just PostgreSQL.
- Database connections are now lazy instead of eager, which should result in less overhead on HTTP requests that don't use additional databases. (In our cabins, this affects nothing.)
- #67: Blog posts are now linked from the Bridge.
- #69: Added a dedicated log for the auto-updater.
- #70: Added a faster install option for deploying an Airship in a hurry, with the sane defaults we provide.
- #77: Fixed responsive UI/UX warts (i.e. small links and buttons).
- #80: If the GD extension isn't loaded, render QR codes for two-factor authentication as SVG instead.
- #88: The installer now uses Zxcvbn to enforce a minimum password strength for administrator accounts.
- Added more input filters for one-dimensional arrays consisting of a static
type (e.g.
int[]
will return a one-dimensional array of integers). - Unhandled exceptions now display a static page outside of debug mode, which is a little better for UX than a white page.
- i18n - run parameters through HTMLPurifier (with caching) to prevent future
XSS payloads in case someone forgets to escape these parameters. HTML is
still allowed, so if you're inserting in an HTML attribute, use the
|e('html_attr')
filter on your input. - Use the correct POST index in account recovery.
- Treat SVG and XML files as plaintext, to prevent stored XSS. Reported on HackerOne.
- Send
Content-Security-Policy
headers on file downloads as well as web pages. Just in case another file type exists in the world that executes JavaScript when the file is viewed.
- Fixed
E_NOTICE
s with the auto-updater. - Identified a bug in the backend server that wasn't publishing commit hashes in CMS Airship core updates. Going forward, the commit hash should be included in each release.
- Only allow HTTP and HTTPS URLs in blog comments.
- Proactively mitigate stored XSS in other invocations of
__()
.
- Fixed a stored XSS vulnerability introduced by using
sprintf()
, which bypassed Twig's autoescape. HackerOne report.
Fixes for bugs reported by @kelunik and @co60ca.
- #61: Comments need a min-height attribute.
- #62, #64: The default configuration is wrong.
- #66: The default configuration broke the 2FA page.
- #41:
Don't raise an
E_NOTICE
upon receiving an invalid CSRF token. - #42: We now have a Dockerfile for easy deployment. Thanks @kelunik and @co60ca.
- #47: If you make a typo when filling in the database credentials on first run, it will no longer proceed silently then fail catastrophically in the last step.
- #50: Display the correct version in the Installer.
- #56: If libsodium is not set up correctly, show an error page explaining the problem and guiding the user towards the solution. Thanks @co60ca.
- Various user interface improvements based on feedback from the initial launch.
- You can now pass an input filter to
$this->post()
from a landing and it will be enforced upon the POST data. If a type error occurs, it simply returnsfalse
. - Fixed a bug that prevented CAPTCHAs from loading on static blog posts. Thanks @kyhwana for reporting this.
- The "parent category" select box now renders properly.
- The authors' photos menu is properly prepopulated by the contexts we use in Airship. Extensions are free to supply their own contexts.
- Fixed a default configuration issue which caused Cabins to be disabled.
- Improved the UX of the Installer. Populate more default settings.
- Fixed a syntax error that snuck into our installer SQL code.
- You can now move or rename directories in our custom page system.
- Added an AJAX endpoint for clearing the cache remotely.
- Fixed Javascript race conditions that prevented the rich text editor from loading reliably.
- Cabins, Motifs, and Gadgets can now be disabled (and remain installed).
- Cabins, Motifs, and Gadgets can now be uninstalled.
- Added a help/support page that displays system information (for privileged users only) and links to the documentation and this Github repository.
- Administrators can post announcements which show up on the Bridge dashboard when users log in. Once a user has read an announcement, they may dismiss the message.
- Bugfix: The Content-Security-Policy management tools didn't allow users to
allow
data:
URIs because of a Twig template error. Instead of slicing at[-4:]
, we were slicing at[4:]
.
- Implemented a secure account recovery implementation, wherein users can opt out of account recovery entirely, or supply a GPG public key. We send a random, short-lived token to the email address on file (since Airship doesn't store plaintext passwords). If a GPG public key is available, their account recovery email will be encrypted by GnuPG.
- Turned all of the Cabin classes into Gears, so that Gadgets can extend their functionality.
- Gadgets can also override the selected Lens, transparently.
- Added the option to cache blog posts and blog listings. If cached, comments will be loaded from AJAX instead of in the page itself. This should allow a single blog post to handle over 10,000 requests per second without a sweat.
- Updated jQuery to 3.0.0.
- Regenerate session IDs on login. Thanks @kelunik for bringing this oversight to our attention.
- Implemented progressive rate-limiting based on two factors: IP subnet and username. This covers both the login form and the account recovery form.
- You can now specify HPKP headers on a per-Cabin basis, via the Cabin Management screen.
- You can now add/remove Cabins, Gadgets, and Motifs from the Bridge.
- Sysadmins can "lock" installs to prevent an admin account compromise from
leading to a vulnerable extension from being installed and subsequently
used by an attacker to compromise the server. Locks come in two varieties:
- Password-based locks, where you must enter a separate password to install a new extension.
- Absolute locks, which can only be removed by the sysadmin.
- In Landings,
$this->lens()
will now terminate script execution. If you need to fetch the output (e.g. for caching), use$this->lensRender()
instead. - Implemented input filters which work on multidimensional arrays (e.g
$_POST
). We provide a few examples (one for each cabin's custom config and one for the universal config). - Implemented optional Two-Factor Authentication support via TOTP (e.g. Google Authenticator).
- Airship now supports in-memory caching via APCu instead of the filesystem.
- Comments are now loaded with AJAX when you elect to cache a blog post.
- When you delete a custom directory, you can elect to create redirects automatically to guide your passengers to the correct destination.
- Bugfix: The Airship Installer failed to assign a default "guest group" which caused file downloads to fail when not authenticated.
- Added a WYSIWYG editor (dubbed "Rich Text" to users).
- Fix CSS and symlink issues from first squashed commit.
- Fixed router bugs. Now
bridge.example.com
andexample.com/bridge
are both acceptable ways to access the bridge (this decision is left to user configuration, of course). - Bump minimum Halite version to
2.1
. - Implemented Keyggdrasil, an Airship-exclusive protocol that allows us to guarantee that all Airships have the same public key and package update history. This is accomplished by a peer verification mechanism.
- Improved Airship Installer workflow.
- Added command line scripts to install new Cabins, Gadgets, and Motifs.
- Allow users to select their preferred Motif for each Cabin.
- Removed validity periods from signing keys. We'll use revocation instead.
- Add more security headers out-of-the-box:
- X-Frame-Options
- X-XSS-Protection
- Improved static page caching (now also sends Content-Securiy-Policy headers).
- Added a
HiddenString
class to hide passwords from stack traces. - Use Ed25519 signatures to mitigate Hash-DoS from untrusted JSON inputs.
- Added configuration option to cache Twig templates.
- Users can now delete blog posts.
- Users can now diff two versions of a blog post.
- Users can now add/remove other users to the same Author.
- Users can now selected uploaded image files to use for biography images and avatars to accompany their blog comments.
- Lots of reorganization, refactoring, and clean-up.
- Moved the CMS Airship Documentation to its own dedicated git repository.
- When you change a blog post's slug, you can optionally create an HTTP 301 redirect to the new URL to prevent visitors from getting an unfortunate HTTP 404 error. This allows you to funnel traffic towards a meaningful destination.
- Implemented the redirect management section. Now you can edit/delete custom URL redirects (some of which are created when you delete/rename content).
- Greatly improved the comment system; now you may reply to other comments.
Built a CMS with security in mind:
- Ed25519-signed automatic updating, powered by Halite
- Argon2i password hashing
- Prepared statements to prevent SQLi
- Context-sensitive escaping (via Twig)
- Integrated with CSPBuilder, plus a web UI to manage the rules
- CSRF Prevention baked in
- Secure long-term authentication
- Incredibly powerful and flexible access controls (whitelist-based)
- Separate authentication (users) from public identities (authors)