CodeSOD: Zoning Out
Matthew D was recently helping a friend plan a trip to Europe from the US. After shopping around a bit, they found a rather affordable airline based out of Poland that fit both their budget and their destination, and started booking a flight.
It was going well until it came time for them to enter their passport information. The site complained that the expiry date was invalid. Since that complaint was happening without a page reload, Matthew was pretty sure it was a buggy bit of client-side validation, so he pulled up the dev tools and poked around.
var validExpiryDate = function(val, fieldNode, ruleValue){ var idField=fieldNode.attr("id").split("_"); var idPax=idField[idField.length-2]; var nDoc=idField[idField.length-1]; var type_document = A.one('#_retrievepnr_WAR_lotairwaysportlet_change-type-document_'+idPax+'_'+nDoc);if (type_document!=null && type_document.val() == "") {return true;}if (val.length <= 8) {var day_value = A.one('#_retrievepnr_WAR_lotairwaysportlet_change-expiry-day_'+idPax+'_'+nDoc).val();var month_value = A.one('#_retrievepnr_WAR_lotairwaysportlet_change-expiry-month_'+idPax+'_'+nDoc).val();var year_value = A.one('#_retrievepnr_WAR_lotairwaysportlet_change-expiry-year_'+idPax+'_'+nDoc).val();if ((day_value == "" && (month_value != "" || year_value != "")) || (month_value == "" && (day_value != "" || year_value != ""))|| (year_value == "" && (day_value != "" || month_value != ""))) {return false;} else if (day_value == "" && month_value == "" && year_value == "") {return false;}if (day_value.length < 2) {day_value = '0' + day_value;}if (month_value.length < 2) {month_value = '0' + month_value;}var date_select = new Date(Date.parse(year_value + "-" + month_value + "-" + day_value + "T00:00:00Z"));var day_select1 = date_select.getDate();var month_select1 = date_select.getMonth() + 1;var year_select1 = date_select.getFullYear();if (day_select1.length < 2) {day_select1 = '0' + day_select1;}if (month_select1.length < 2) {month_select1 = '0' + month_select1;}if (day_value != day_select1 || month_value != month_select1 || year_value != year_select1) {return false;}var ageDifMs = Date.now() - date_select.getTime();var ageDate = new Date(ageDifMs);var age = Math.abs(ageDate.getUTCFullYear() - 1970);var isNotExpired = Date.now() < date_select.getTime();if (isNotExpired) {return true;} else {return false;}} else {return false;} }
There's a lot of ugly in this code, with how it's grabbing DOM elements, how it's handling the validation and padding of the date input fields. It's hard to tell from just looking at the code, but the purpose is to make sure that the expiration date is a valid date in the future.
Someone once heard that comparing against timezones was dangerous, so they decided to coerce the date into the same timezone:
var date_select = new Date(Date.parse(year_value + "-" + month_value + "-" + day_value + "T00:00:00Z"));
That puts it all at UTC+0. Even there, that raises a curious boundary condition: if my passport expires on, say, 4/30/19 but was issued in UTC+5, is it still valid on 4/29/19 at 10PM in a location at UTC+0? I'm sure there are rules about that, and I'm sure the developer wasn't actually thinking about those rules at all.
And that's why, at the very end, their method of checking is simply Date.now() < date_select.getTime(). date_select contains a date in UTC+0. Date.now() will always contain a date in your local TZ.
Now, in most cases, that's probably not a serious problem, and the "key" not expired check- var isNotExpired = Date.now() < date_select.getTime(); will work most of the time.
The problem, and the real WTF, is that before doing that check, they also do this check:
if (day_value != day_select1 || month_value != month_select1 || year_value != year_select1) {return false;}
Here, they compare: if the value that came in from the form fields don't match the values in our time-zone converted date, their expiry is invalid.
Which, if you happen to be say, in UTC-8 and you're booking your flight in the evening, it's already the next day in UTC+0, so this won't pass. In fact, if you're in any TZ other than UTC+0, and you try and book your flight at the wrong time, it won't pass.
Of course, since this airline is based out of Poland, their home base is UTC+1, so this bug would almost *never* trigger there. Almost never.
[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!