The Daily WTF

Subscribe to The Daily WTF feed
Curious Perversions in Information Technology
Updated: 1 hour 16 min ago

CodeSOD: Totally Valid

11 hours 25 min ago

Greg's co-worker really wanted to make sure that a variable was correctly set to true or false. So they did this:

if (isValid == true) { isValid = true; } else { isValid = false; }

If isValid is true, set it to true, otherwise set it to false. isValid is a boolean in C#, so the only options it could have are true and false.

The real WTF is that in languages with truthiness, this code may actually be useful- converting a falsey value to a literal false. But in C#, this is both useless and stupid.

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
Categories: Computer

CodeSOD: Metaception

Wed, 2024-05-01 08:30

Meta-programming- programs which generate programs- is a delightful hobby, but usually shouldn't be used in production code. Usually. I mean, if you're working in LISP, 90% of your program is going to be macros.

But if you're using PHP and JavaScript, there's good odds that someone you work with has decided to combine these two tastes together to create something nobody wants to taste.

This is a depressingly common anti-pattern, usually seen in the form of JavaScript code which looks like: if (1 == 2)- once upon a time one or both of those values were PHP variables, but we only see the resulting generated code.

A more extreme version happened to our anonymous submitter.

<div class="w-box-content"> <? table(array( //... 'options'=> array( <script type="text/javascript"> {if $doc_status=="edit"} $("#doc-{$id}").closest("tr").find("td,a").addClass("text-danger"); {/if} </script>' ) )); ?> </div>

We start with some HTML which wraps some PHP code. The PHP code embeds an HTML tag for JavaScript into an array, and inside that JavaScript embeds a Smarty Template, which wraps some JavaScript code that modifies a CSS class.

"text-danger", indeed. It's an impressively nested system of different languages and tools.

As an aside, I was unfamiliar with Smarty templates, so I looked them up. I won't comment on the technology, but for a PHP library framework, they also have a slew of "partners" for monetization- mostly online casinos and scams that'll invest your IRA into gold. I won't link them, but at first I thought their page had been hacked and turned into a link farm. No, they're still updating (as of mid 2023).

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

CodeSOD: Finding the Right Size

Tue, 2024-04-30 08:30

Zeke sends us a C# snippet from an extract-transform-load process his company uses. It's… special.

private void ResizeColumn(string table, string column, int minSize) { if(null == _connection) return; string sqlReadSize = "SELECT DATA_LENGTH,DATA_TYPE,DATA_PRECISION,DATA_SCALE FROM USER_TAB_COLS WHERE TABLE_NAME = '" + table.ToUpper() + "' AND COLUMN_NAME = '" + column.ToUpper() + "'"; string data_length = ""; string data_type = ""; string data_precision = ""; string data_scale = ""; string sizeInfo = minSize.ToString(); IDataReader r = null; try { r = _connection.DbAccessor.ExecuteSqlText.ExecuteReader(sqlReadSize); if(null != r && r.Read()) { if(!r.IsDBNull(0)) data_length = Convert.ToString(r[0]); if(!r.IsDBNull(1)) data_type = Convert.ToString(r[1]); if(!r.IsDBNull(2)) data_precision = Convert.ToString(r[2]); if(!r.IsDBNull(3)) data_scale = Convert.ToString(r[3]); r.Close(); r = null; } } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return; } finally { if(null != r) { r.Close(); r = null; } } if(data_type == "NUMBER") { return; } if(data_type == "DATE") { return; } if(data_type == "CLOB") { return; } if(data_type == "BLOB") { return; } if(minSize <= Convert.ToInt32(data_length)) { return; } string sqlAlterSize = "ALTER TABLE " + table + " modify " + column.ToUpper() + " " + data_type + "(" + sizeInfo + ")"; try { _connection.DbAccessor.ExecuteSqlText.ExecuteScalar(sqlAlterSize); } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return; } }

Let's trace through this. We start by failing silently if the database connection hasn't been initialized, which is sure to make debugging a real treat. Then we prepare a string-concatenated query into the databases's data dictionary, opening up a lovely SQL injection attack (which, in their defense, I don't expect the inputs to this function to be user driven, but still).

We run the query and populate local variables with the results- local string variables. The source data in the database is numeric (this is, by the by, an Oracle database)- so we just discard that and convert it to strings.

There's a lovely bonus of a double-close- they close the reader in the try portion and the finally portion, instead of just using the finally. More fun, they also do the useless null assignment that people who don't understand garbage collection often do.

Then, we check the type of the column in question. If it's a number, a date, a CLOB or a BLOB, we do nothing. This is really fun because there are plenty of built-in datatypes that don't have a size field, but aren't included here- TIMESTAMP for example, expects fractional seconds precision, not a size. We won't change the size of a NUMBER (which takes precision and scale), but will change the precision of a FLOAT.

What they really wanted was to just change the sizes of CHAR, NCHAR, VARCHAR2, and NVARCHAR2s. But instead of saying that, they opted to try and come at it the back way around, and created a method that's almost certain to create explosions at some point.

Finally, they execute an ALTER TABLE command to do the update. Any failures are treated as a debugging issue, which again, is going to be great when you're trying to decide why this failed in production.

Not only is the method horrible, but it's also unnecessary. Once upon a time, someone recognized that they needed to hold text fields of arbitrary lengths and configured all the text columns to be 4000 bytes (the default maximum size in Oracle), which was much larger than any of the input data.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

CodeSOD: Article tF7q2

Mon, 2024-04-29 08:30

When I first saw Nick L's submission, I thought to myself, "This is just decompiled code, so of course the names are bad."

Public Function tF7q2() As String Dim SelectedtF7q2 As String = Request.QueryString("tF7q2") tF7q2 = SelectedtF7q2 End Function

I mean, a name like tF7q2 can't be human generated, right? But then, the query string also contains tF7q2. And honestly, I think most reverse engineering tools would give you a more useful name than this.

I'll let Nick explain a bit more:

This is a snippet from a legacy application that is currently gobbling up all available disk space on a server. No, the developer who made it is no longer at the company. Yes, everyone wants this application dead. Don't worry, this function is referenced 5 times with no explanation to what tF7q2 means.

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Categories: Computer

Error'd: Enterprising Michael

Fri, 2024-04-26 08:30

Faithful Michael R. is good for a chuckle today. "I am using the free tier Infura right now but think I will go enterprisey straight away." Can't turn down a deal like that, eh?

 

Chris M. sent in a captcha that is neither an error nor a wtf, but it's kind of funny so here you go: "Microsoft obviously doesn't use Google's CAPTCHA service, but whatever system they've got is hinting at a competitor to Xbox and a certain fruity laptop manufacturer's new in-house silicon."

 

Cyclist jeffphi crashed his watch. Better that than the bike! "Looks like the Strava app on my Pixel watch has run into a conundrum. Guess I'll never have the data to prove the sheer athleticism I demonstrated on that lunchtime bike ride."

 

A previous poster returned this week with this: "I recently submitted an anonymous post to Error'd with a strangely formatted Customer Satisfaction Survey from Postman. I've since received the attached follow-up." I suspect this is a hinky bit of code that's supposed to be adaptive for mobile sites, and so it's choosing to stack the options instead of crowding them more closely together or reducing their size. In any case, it's surprising this cleared basic testing.

 

Michael T. "Synchrony Bank has introduced the new YY-MM-DDDD date format, but didn't synchronize with themselves." This is cute, and undoubtedly just one more of the myriad failures that javascript programming is capable of.

 

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Categories: Computer

CodeSOD: Unaccountable Counting

Thu, 2024-04-25 08:30

Ulvhamne sends us some bad code that, well, I think at this point we should really coin a name for this particular anti-pattern.

@Override public int getNumOfItemsInDataContainer(int parDataId) { int numberOfItems = 0; for (Integer x : myTransactionDataContainerMap.keySet()) { numberOfItems ++; } return numberOfItems; }

This C# function wants to get the number of items contained in a dictionary. To do that, it iterates across the set of keys, and increments a counter. This is instead of using the size field that's part of the dictionary container.

This one adds a lovely bonus of taking a parameter parDataId, and doing nothing with it.

Marking the parameter as par is an example of so-called "Apps Hungarian", where the prefix is meant to represent the "logical" datatype or purpose of the variable- semantic information, instead of type information. While marginally better than iDataId, I still hate it. Also, if your methods have so many local variables in scope that you need to distinguish them from the parameters somehow, lest you get confused, that's an even bigger code smell.

All the Hungarian nonsense aside, this is an example of the very common anti-pattern of manually counting the items in a container when the container already can report that information.

So what do we call that? "Container Countification"? "Fingers and Toes"? "Sesame Street Counting"?

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: Query Query Query

Wed, 2024-04-24 08:30

Bob's employer had a data-driven application which wasn't performing terribly well. They had some in-house database administrators, but their skills were more "keep things running," and less "do deep optimizations". The company opted to hire a contract DBA to come in, address the performance problems, and leave.

In actual fact, the DBA came in, ran some monitoring, and then simply wrote some guidance- generic, and frankly useless guidance. "Index on frequently queried fields," and "ensure database statistics are gathered on the appropriate schedule."

The only meaningful output of the contractor's visit was a list of the badly performing stored procedures. Bob was given the list and told, "Go fix these."

One stored procedure needed to populate variables with data from the order table. Specifically, it needed to collect sender_id, user_group, and file_ref from the orders table. Here's how it did that:

if (@origin = -2 or @origin = -4 or @origin = -5 or @origin = -6 or @origin = -7 or @origin = -8 or @origin = -9) begin select @id = sender_id from order where ref = @mur select @group = user_group from order where ref = @mur if (@ltaId is null) begin select @ltaId = null end select @file_ref = file_ref from order where ref = @mur select @ref = ref from order where mur = @mur end else begin select @id = sender_id from order where mur = @mur select @group = user_group from order where mur = @mur if (@ltaId is null) begin select @ltaId = null end select @file_ref = file_ref from order where mur = @mur select @ref = ref from order where mur = @mur end

Let's start with the if condition. We never love magic numbers, but that's hardly a WTF. The real problem is that the two branches differ only in whitespace. There's no reason for the if. Perhaps once upon a time there was, but there isn't now.

Now, all the fields we need are in the table order, they're all selected by the field mur which is a unique key, but for some reason, this code needs to run the query three times to get its data. Also, mur is a unique key in the domain but not in the database- no unique constraint or index was applied, which is part of the reason performance was as bad as it was.

But the real highlight, for me, is how @ltaId gets set. If it's null, set it equal to null. That's a ^chef's kiss^ right there. Beautiful(ly horrible).

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
Categories: Computer

CodeSOD: An Obsolete Approach

Tue, 2024-04-23 08:30

Marcus's team was restructuring the API, and the architect thus wanted a number of methods marked obsolete, to encourage developers to move to the new version of the API. So the architect created a Jira task, assigned it to a dev, and moved on.

Somehow, this C# code got committed and merged, despite being code reviewed:

public static void OBSOLETE_PopulateOfferNodes(Ecometry.New.Model.CategoryHierarchyNode _mainNode, ref Ecometry.New.Model.CategoryHierarchyNode _defaultOffersNode, ref Ecometry.New.Model.CategoryHierarchyNode _foundOffersNode) { foreach (Ecometry.New.Model.CategoryHierarchyNode offersNode in _mainNode.Children) { if (offersNode.Category.Name.ToUpper().Trim().Equals("_DEFAULT")) _defaultOffersNode = offersNode; if (offersNode.Category.Name.ToUpper().Trim().Equals(Ecometry.New.Impl.ExecutionContextFactory.ExecutionContext.CurrentCart.SourceCode.Code.ToUpper().Trim()) && Snapshot.OfferRelatedUtilities.isDateOK2Show(offersNode)) _foundOffersNode = offersNode; if (_defaultOffersNode != null && _foundOffersNode != null) break; } return; }

Now, what the architect had meant was that the developer should use the [Obsolete] attribute, which lets you annotate methods like so:

[Obsolete("Foo is deprecated, please use Bar instead")] public void Foo() {}

What the developer heard, instead, was that they should do a find and replace of every use of the method name and prepend OBSOLETE_ to it.

The annotation has a number of advantages: unlike the name, it produces a compiler warning, and that warning can be promoted to a compiler error in future versions, without fully removing the method. It also helpfully avoids this drunken form of Hungarian notation that sneaks what is essentially version information into the method name.

This is the first step on a path that ends with methods named New_UseThisOne_Foo().

All that aside, the if statements are a mouthful in there. They're not a WTF, but… boy, they could probably be simplified for readability. I suspect that's part of why this method is obsolete.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: Concrapenate Strings

Mon, 2024-04-22 08:30

As oft discussed, null-terminated C-style strings are an endless source of problems. But there's no problem so bad that it can't be made worse by a sufficiently motivated developer.

Today's rather old code comes from Mike, who inherited an old, MFC application. This code is responsible for opening a file dialog, and the key goal of the code is to configure the file filter in that dialog. In MFC, this is done by passing a delimited string containing a caption and a glob for filtering. E.g., "Text Files (.txt) | *.txt" would open a dialog for finding text files.

char szFileName[MAX_FILE_NAME]; char szDlgTitle[] = "Save File As"; char szDefExt[] = "log"; char* szPos; WORD nFileOffset; WORD nFileExtension; char szFileFilter[MAX_FILE_NAME]; char szBuffer[128]; std::ifstream inFile; memset(szFileFilter, NULL, MAX_FILTER_SIZE); memset(szFileName, NULL, MAX_FILE_NAME); strcpy_s(szFileFilter, sizeof(szFileFilter), "*.log - Debug Log File|"); szPos = szFileFilter + strlen("*.log - Debug Log File|") + 1; strcpy_s(szPos, sizeof( szPos), "*.log"); if(!OpenSaveFile(TRUE, szFileName, MAX_FILE_NAME, szFileFilter, szDlgTitle, nFileOffset, nFileExtension, szDefExt, hWndInstance, hWndMain)) break;

The impressive thing about this code is that this was released, sent to customers, and crashed consistently- until people started using the 64-bit build, when it started working again.

After declaring some variables, we start by using memset to null out some character arrays. This isn't particularly necessary, but it's mostly harmless- or at least it would be if they actually read their own code.

szFileFilter is declared using the size MAX_FILE_NAME, but when set to null, a space equal to MAX_FILTER_SIZE is used. If MAX_FILTER_SIZE is less than or equal to MAX_FILE_NAME this is fine- but if it's ever larger- welp, we've got an out of bounds access.

That's not what guarantees a crash, that's just bad practice.

Next, we use strcpy_s to copy the caption into our szFileFilter array. Then we calculate an offset within that array, to store in szPos. We then use strcpy_s again, copying our filter in to the end of the string.

This is the line that's guaranteed to crash. Because note that they pass a size to strcpy_s- sizeof(szPos). szPos is a pointer, not an array. So unlike all the other strings used in this example, sizeof won't tell you its length, it'll just tell you how much memory a pointer takes up. On 32-bit, that'd be 4 bytes. On 64-bit, that'd be 8. And that's why it suddenly started working when people changed builds.

Also, the ideal-world version of Hungarian notation is that, by specifying the types in the variable name, you can defend against these errors- but because they used sz for all strings, whether stored in allocated arrays or as pointers, they didn't have the information they needed.

Also, instead of doing all this weird string offset stuff with multiple copies, or doing any strcat_ss, they could have just…

strcpy_s(szFileFilter, sizeof(szFileFilter), "*.log - Debug Log File|*.log); [Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Categories: Computer

Error'd: Believe It Or Not

Fri, 2024-04-19 08:30

This week we have a special visit from a mythical beast: the snarklemma. But first, a non-error Error'd.

Obsessive Optimizer Ian K. "Walmart's nationwide network of warehouse stores means they can save time and money by shipping locally. FedEx has them covered: Their nationwide shipping fleet determined the shortest path from Houston to its largest suburb goes via Georgia." Not the shortest path, nor the fastest, but surely the cheapest one that meets the delivery date requirement. It's probably not an error, and I believe it, but I still can't believe it!

 

Possibly our most faithful contributor, Michael R. is also a Voyager fan. He shared this snapshot with us, commanding "Adjust our delefctor!" I think that's probably just the way they spefl thingf int he 24f sentry.

 

An anonymous poster shared this image with us, remarking: "I'm not sure..."

 

A second anonymous poster was caught on the horns of a snarklemma. Please help them out by voting in the comments. Would you prefer snark A: "Disney is acquiring so many things nowadays. I didn't know it was also acquiring Android notifications." Or would you prefer snark B: "I only have one TV and only one Disney+ account playing only one animated series. And yet, Google Play Services generated several notifications. Thats not Disney+, that's Disney+++++".

 

Miquel B. eschews Netflix in favor of the edgier material available elsewhere. "Someone posted Lorem Ipsum for their review of the series." I couldn't bear to watch it; I have test anxiety.

 

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: A List of Mistakes

Thu, 2024-04-18 08:30

Yesterday we talked about bad CSS. Today, we're going to talk about bad HTML.

Corey inherited a web page that, among other things, wanted to display a bulleted list of links. Now, you or I might reach for the ul element, which is for displaying bulleted lists. But we do not have the galaxy sized brains of this individual:

<table style="font-family: Verdana;"> <tr> <td valign="top"> • </td> <td> Google <br /> <a href="http://www.google.com" target="_blank">http://www.google.com</a> </td> </tr> <tr> <td valign="top"> • </td> <td> Yahoo <br /> <a href="http://www.yahoo.com" target="_blank">http://www.yahoo.com/</a> </td> </tr> <tr> <td valign="top"> • </td> <td> Bing <br /> <a href="http://www.bing.com" target="_blank">http://www.bing.com</a> </td> </tr> </table>

Here, they opted to use a table, where each list item is a row, and the bullet is a literal • symbol in the code.

For web developers of a certain age, we remember when laying out your entire page inside of tables was a common practice. This let you easily define distinct header, sidebar, and footer sections in an era before CSS and divs everywhere.

But they were never meant to be used like this.

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Categories: Computer

CodeSOD: Classical Design

Wed, 2024-04-17 08:30

There is a surprising amount of debate about how to use CSS classes. The correct side of this debate argues that we should use classes to describe what the content is, what role it serves in our UI; i.e., a section of a page displaying employee information might be classed employee. If we want the "name" field of an employee to have a red underline, we might write a rule like:

.employee .name { text-decoration: underline red; }

The wrong side argues that this doesn't tell you what it looks like, so we should have classes like red-underline, because they care what the UI element looks like, not what it is.

Mark sends us some HTML that highlights how wrong things can go:

<div class="print ProfileBox floatRight" style="background-color:#ffffff;" > <div class="horiz"> <div class="horiz"> <div class="vert"> <div class="vert"> <div class="roundGray"> <div class="roundPurple"> <div class="roundGray"> <div class="roundPurple"> <div class="roundGray"> <div class="roundPurple"> <div class="roundGray"> <div class="roundPurple"> <div class="bold"> Renew Profile <br><br> Change of Employer <br><br> Change of Position <br><br> Print Training Reports <br><br><br><br> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> <div class="floatRight">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div> <div class="ProfileBox print floatRight" style="background-color:#ffffff" > <div class="horiz"> <div class="horiz"> <div class="vert"> <div class="vert"> <div class="roundGray"> <div class="roundPurple"> <div class="roundGray"> <div class="roundPurple"> <div class="roundGray"> <div class="roundPurple"> <div class="roundGray"> <div class="roundPurple"> <div class="bold"> Profile Status: <br><br> Registry Code: <br><br> Career Level: <br><br> Member Since: <br><br> Renewal Date: <br><br> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div>

It's worth noting, this was not machine-generated HTML. The developer responsible wrote this code. Mark didn't supply the CSS, so I have no idea what it would display like, but the code looks horrible.

Mark adds:

A colleague of mine committed this code. This is the perfect example of why you define your CSS classes based on the content, not the styling they apply.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: A Small Partition

Tue, 2024-04-16 08:30

Once upon a time, I was tuning a database performance issue. The backing database was an Oracle database, and the key problem was simply that the data needed to be partitioned. Great, easy, I wrote up a change script, applied it to a test environment, gathered some metrics to prove that it had the effects we expected, and submitted a request to apply it to production.

And the DBAs came down on me like a sledgehammer. Why? Well, according to our DBAs, the license we had with Oracle didn't let us use partitioning. The feature wasn't disabled in any way, but when an Oracle compliance check was performed, we'd get dinged and they'd charge us big bucks for having used the feature- and if we wanted to enable it, it'd cost us $10,000 a year, and no one was willing to pay that.

Now, I have no idea how true this actually was. I have no reason to disbelieve the DBAs I was working with, but perhaps they were being overly cautious. But the result is that I had to manually partition the data into different tables. The good news was all the writes always went into the most recent table, almost all of the reads went to either the current table or last month's table, and everything else was basically legacy and while it might be used in a report, if it was slower than the pitch drop experiment, that was fine.

It was stupid, and it sucked, but it wasn't the worst sin I'd ever committed.

Which is why I have at least some sympathy for this stored procedure, found by Ayende.

ALTER PROCEDURE GetDataForDate @date DATETIME AS DECLARE @sql nvarchar(max) SET @sql = 'select * from data_' + convert(nvarchar(30),getdate(),112) EXEC sp_executesql @sql

Now, this is for an MS SQL database, which does not have any weird licensing around using partitions. But we can see here the manual partitioning in use.

There are a set of data_yyyymmdd tables. When we call this function, it takes the supplied date and writes a query specific to that table. This means that there is a table for every day.

Ayende got called in for this because one of the reports was running slowly. This report simply… used all of the tables. It just UNIONed them together. This, of course, removed any benefit of partitioning, and didn't exactly make the query planning engine's job easy, either. The execution paths it generated were not terribly efficient.

At the time Ayende first found it, there were 750 tables. And obviously, as each day ticked past, a new table was created. And yes, someone manually updated the view, every day.

Ayende sent this to us many tables ago, and I dread to think how many tables are yet to be created.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: A Top Level Validator

Mon, 2024-04-15 08:30

As oft stated, the specification governing email addresses is complicated, and isn't really well suited for regular expressions. You can get there, but honestly, most applications can get away with checking for something that looks vaguely email like and call it a day.

Now, as complicated as the "accurate" regex can get, we can certainly find worse regexes for validating emails. Morgan did, while on a contract.

The client side had this lovely regex for validating emails:

/* Check if a string is in valid email format. Returns true if valid, false otherwise. */ function isEmail(str) { var regex = /^[-_.a-z0-9]+@(([-_a-z0-9]+\.)+(ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|in|info|int|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mil|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nt|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5])\.){3}([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$/i; return regex.test(str); }

They check a long list of TLDs to ensure that the email address is potentially valid, or accept an email address. Is the list exhaustive? Of course not. There are loads of TLDs not on this list- perhaps not widely used ones, but it's incomplete. And also, unnecessary.

But not so unnecessary that they didn't do it twice- they mirrored this code on the server side, in PHP:

function isEmail($email) { return(preg_match("/^[-_.[:alnum:]]+@((([[:alnum:]]|[[:alnum:]][[:alnum:]-]*[[:alnum:]])\.)+(ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|in|info|int|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mil|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nt|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)$|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5])\.){3}([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$/i" ,$email)); }

Bad code is even better when you have to maintain it in two places and in two languages. I suppose I should just be happy that they're doing some kind of server-side validation.

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!
Categories: Computer

Error'd: Paycheque

Fri, 2024-04-12 08:30

There are an infinite variety of ways to be wrong, but only very small number of ways to be right.

Patient Peter W. discovers that MS Word is of two minds about English usage. "Microsoft Word just can't seem to agree with itself on how to spell paycheck/pay check." Faithful readers know it's even worse than that.

 

Slow Daniel confused me with this complaint. He writes "It seems that the eclipse has reversed the flow of time for the traffic-free travel time." I don't get it. Can readers explain? The only WTF I see here is how much faster it is to walk than to drive 2 miles. Franconia isn't Conway!

 

Parsimonious Adam found a surprise discount. "This album was initially listed as pay-what-you-want. I tried to pay $4 for it, but the price changed to a minimum of $5 before I was able to check out, and checkout rightfully failed. My shopping cart then reverted to saying this." I want to know what happened next.

 

Some of you dislike it when I thaw a thematic item from the deep freeze, so please note that B.J. sent us this sometime last year, noting that "Time works in mysterious ways for some companies. I earned a Verizon community 2 Year badge 36 hours after earning a 4 Year badge." I did consider saving it until July 26 this year but I'm just not that patient.

 

By comparison, I only had to drag this entry from the far back bottom corner of February's fridge.
I imagine chill Paul intoning with his radio voice "This next saturday is going to be all ice ice baby."

 

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
Categories: Computer

CodeSOD: To Tell the Truth

Thu, 2024-04-11 08:30

So many languages eschew "truth" for "truthiness". Today, we're looking at PHP's approach.

PHP automatically coerces types to a boolean with some fairly simple rules:

  • the boolean false is false
  • the integer 0 is false, as is the float 0.0 and -0.0.
  • empty strings and the string "0" are false
  • arrays with no elements are false
  • NULL is false
  • objects may also override the cast behavior to define their own
  • everything else is true

Honestly, for PHP, this is fairly sane and reasonable. The string "0" makes my skin itch a bit, and the fact that the documentation page needs a big disclaimer warning that -1 is true hints at some people blundering as they convert from other languages.

But we're not doing a language critique. We're looking at a very specific block of code, which Jakub inherited. You see, someone didn't like this, so they implemented their own version:

protected static function emulate_filter_bool( $value ) { // @codingStandardsIgnoreStart static $true = array( '1', 'true', 'True', 'TRUE', 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON', ); static $false = array( '0', 'false', 'False', 'FALSE', 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF', ); // @codingStandardsIgnoreEnd if ( is_bool( $value ) ) { return $value; } elseif ( is_int( $value ) && ( 0 === $value || 1 === $value ) ) { return (bool) $value; } elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( (float) 0 === $value || (float) 1 === $value ) ) { return (bool) $value; } elseif ( is_string( $value ) ) { $value = trim( $value ); if ( in_array( $value, $true, true ) ) { return true; } elseif ( in_array( $value, $false, true ) ) { return false; } else { return false; } } return false; }

I love when a code block starts with an annotation to force the linter to ignore them. Always great.

We start by declaring arrays of a variety of possible true and false values. Most of the variations are in capitalization, but not exhaustive variations in capitalization, which I'm sure will cause problems at some point. But let's see how this code works.

First, we check if the value is a boolean, and if so, return it. Fine, very reasonable.

Second, we check if it's an integer, and if it is either 0 or 1, we cast it to a boolean and return it. In pure PHP, anything non-zero is true, but here only 1 is true.

We then do a similar check for floats- if it's a float, it is a number, and it's either 0 or 1, we cast it to float and return it. Note, however, that they're doing a simple equality comparison- which means that floating point inputs like 1.000000001 will fail this test, but will often still get printed as 1 when formatted for output, causing developers to get very confused (ask Jakub how he knows).

Finally, we check for strings, and do an in_array comparison to check if the value is either true or false. If it's neither true nor false, we simply return false (instead of FILE_NOT_FOUND). This raises the obvious question: why even bother with the check against the false values array, when we can just assume the input is false if it isn't in the true values array?

Jakub has this to add:

This method finds application in numerous places, particularly where front-end interfaces display checkboxes. I truly dread discovering the full extent of the code responsible for that...

ul { list-style-type: disc; list-style-position: inside; } [Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: Terminated By Nulls

Wed, 2024-04-10 08:30

Strings in C are a unique collection of mistakes. The biggest one is the idea of null termination. Null termination is not without its advantages: because you're using a single byte to mark the end of the string, you can have strings of arbitrary length. No need to track the size and worry if your size variable is big enough to hold the end of the string. No complicated data structures. Just "read till you find a 0 byte, and you know you're done."

Of course, this is the root of a lot of evils. Malicious inputs that lack a null terminator, for example, are a common exploit. It's so dangerous that all of the str* functions have strn* versions, which allow you to pass sizes to ensure you don't overrun any buffers.

Dmitri sends us a simple example of someone not quite fully understanding this.

strcpy( buffer, string); strcat( buffer, "\0");

The first line here copies the contents of string into buffer. It leverages the null terminator to know when the copy can stop. Then, we use strcat, which scans the string for the null terminator, and inserts a new string at the end- the new string, in this case, being the null terminator.

The developer responsible for this is protecting against a string lacking its null terminator by using functions which absolutely require it to be null terminated.

C strings are hard in the best case, but they're a lot harder when you don't understand them.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

Two Pizzas for ME

Tue, 2024-04-09 08:30

Gloria was a senior developer at IniMirage, a company that makes custom visualizations for their clients. Over a few years, IniMirage had grown to more than 100 people, but was still very much in startup mode. Because of that, Gloria tried to keep her teams sized for two pizzas. Thomas, the product manager, on the other hand, felt that the company was ready to make big moves, and could scale up the teams: more people could move products faster. And Thomas was her manager, so he was "setting direction."

Gloria's elderly dog had spent the night at the emergency vet, and the company hadn't grown up to "giving sick days" yet, so she was nursing a headache from lack of sleep, when Thomas tried to initiate a Slack huddle. He had a habit of pushing the "Huddle" button any time the mood struct, without rhyme or reason.

She put on her headphones and accepted the call. "It's Gloria. Can you hear me?" She checked her mic, and repeated the check. She waited a minute before hanging up and getting back to work.

About five minutes later, Thomas called again."Hey, did you call me like 10 minutes ago?"

"No, you called me." Gloria facepalmed and took a deep, calming breath. "I couldn't hear you."

Thomas said, "Huh, okay. Anyway, is that demo ready for today?"

Thomas loved making schedules. He usually used Excel. There was just one problem: he rarely shared them, and he rarely read them after making them. Gloria had nothing on her calendar. "What demo?"

"Oh, Dylan said he was ready for a demo. So I scheduled it with Jack."

Jack was the CEO. Dylan was one of Gloria's peers. Gloria checked Github, and said, "Well, Dylan hasn't pushed anything for… months. I haven't heard anything from him. Has he showed you this demo?"

Gloria heard crunching. Thomas was munching on some chips. She heard him typing. After a minute, she said, "Thomas?"

"Oh, sorry, I was distracted."

Clearly. "Okay, I think we should cancel this meeting. I've seen this before, and with a bad demo, we could lose buy in."

Thomas said, "No, no, it'll be fine."

Gloria said, "Okay, well, let me know how that demo goes." She left the call and went back to work, thinking that it'd be Thomas's funeral. A few minutes before the meeting, her inbox dinged. She was now invited to the demo.

She joined the meeting, only to learn that Dylan was out sick and couldn't make the meeting. She spent the time giving project updates on her work, instead of demos, which is what the CEO actually wanted anyway. The meeting ended and everyone was happy- everyone but Gloria.

Gloria wrote an email to the CEO, expressing her concerns. Thomas was inattentive, incommunicative, and had left her alone to manage the team. She felt that she was doing more of the product management work than Thomas was. Jack replied that he appreciated her concerns, but that Thomas was growing into the position.

Julia, one of the other product managers, popped by Gloria's desk a few weeks later. "You know Dylan?"

Gloria said, "Well, I know he hasn't push any code in a literal year and keeps getting sick. I think I've pushed more code to his project than he has, and I'm not on it."

Julia laughed. "Well, he's been fired, but not for that."

Thomas had been pushing for more demos. Which meant he pulled Dylan into more meetings with the CEO. Jack was a "face time" person, and required everyone to turn on their webcams during meetings. It didn't take very many meetings to discover that Dylan was an entirely different person each time. There were multiple Dylans.

"But even without that, HR was going to fire him for not showing up to work," Julia said.

"But… if there were multiple people… why couldn't someone show up?" Gloria realized she was asking the wrong question. "How did Thomas never realize it?"

And if he was multiple people, how could he never get any work done? Dylan was a two-pizza team all by himself.

After the Dylan debacle, Thomas resigned suddenly and left to work at a competitor. A new product manager, Penny, came on board, and was organized, communicative, and attentive. Gloria never heard about Dylan again, and Penny kept the team sizes small.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

CodeSOD: They Key To Dictionaries

Mon, 2024-04-08 08:30

It's incredibly common to convert objects to dictionaries/maps and back, for all sorts of reasons. Jeff's co-worker was tasked with taking a dictionary which contained three keys, "mail", "telephonenumber", and "facsimiletelephonenumber" into an object representing a contact. This was their solution:

foreach (string item in _ptAttributeDic.Keys) { string val = _ptAttributeDic[item]; switch (item) { case "mail": if (string.IsNullOrEmpty(base._email)) base._email = val; break; case "facsimiletelephonenumber": base._faxNum = val; break; case "telephonenumber": base._phoneNumber = val; break; } }

Yes, we iterate across all of them to find the ones that we want. The dictionary in question is actually quite large, so we spend most of our time here looking at keys we don't care about, to find the three we do. If only there were some easier way, some efficient option for finding items in a dictionary by their name. If only we could just fetch items by their key, then we wouldn't need to have this loop. If only.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

Error'd: Past Imperfect

Fri, 2024-04-05 08:30

A twitchy anonymous reporter pointed out that our form validation code is flaky. He's not wrong. But at least it can report time without needing emoticons! :-3

That same anon sent us the following, explaining "Folks at Twitch are very brave. So brave, they wrote their own time math."

 

Secret Agent Sjoerd reports "There was no train two minutes ago so I presume I should have caught it in an alternate universe." Denver is a key nexus in the multiverse, according to Myka.

 

Chilly Dallin H. is a tiny bit heated about ambiguous abbreviations - or at least about the software that interprets them with inadequate context. "With a range that short, I'd hate to take one of the older generation planes." At least it might be visible!

 

Phoney François P. "I was running a return of a phone through a big company website that shall not be named. Thankfully, they processed my order on April 1st 2024, or 2024年4月1日 in Japanese. There is a slight delay though as it shows 14月2024, which should be the 14th month of 2024. Dates are hard. Date formatting is complicated. For international date formatting, please come back later."

 

At some time in the past, the original Adam R. encountered a time slip. We're just getting to see it even now. "GitHub must be operating on a different calendar than the Gregorian. Comments made just 4 weeks ago [today is 2023-02-07] are being displayed as made last year."

 

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!
Categories: Computer

Pages