The Daily WTF

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

The Big Refactoring Update

Wed, 2025-01-29 07:30

Today's anonymous submitter spent a few weeks feeling pretty good about themselves. You see, they'd inherited a gigantic and complex pile of code, an application spread out across 15 backend servers, theoretically organized into "modules" and "microservices" but in reality was a big ball of mud. And after a long and arduous process, they'd dug through that ball of mud and managed to delete 190 files, totaling 30,000 lines of code. That was fully 2/3rds of the total codebase, gone- and yet the tests continued to pass, the application continued to run, and everyone was just much happier with it.

Two weeks later, a new ticket comes in: users are getting a 403 error when trying to access the "User Update" screen. Our submitter has seen a lot of these tickets, and it almost always means that the user's permissions are misconfigured. It's an easy fix, and not a code problem.

Just to be on the safe side, though, they pull up the screen with their account- guaranteed to have the right permissions- and get a 403.

As you can imagine, the temptation to sneak a few fixes in alongside this massive refactoring was impossible to resist. One of the problems was that most of their routes were camelCase URLs, but userupdate was not. So they'd fixed it. It was a minor change, and it worked in testing. So what was happening?

Well, there was a legacy authorization database. It was one of those 15 backend servers, and it ran no web code, and thus wasn't touched by our submitter's refactoring. Despite their web layer having copious authorization and authentication code, someone had decided back in the olden days, to implement that authorization and authentication in its own database.

Not every request went through this database. It impacted new sessions, but only under specific conditions. But this database had a table in it, which listed off all the routes. And unlike the web code, which used regular expressions for checking routes, and were case insensitive, this database did a strict equality comparison.

The fix was simple: update the table to allow userUpdate. But it also pointed towards a deeper, meaner target for future refactoring: dealing with this sometimes required (but often not!) authentication step lurking in a database that no one had thought about until our submitter's refactoring broke something.

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

CodeSOD: Contains Bad Choices

Tue, 2025-01-28 07:30

Paul's co-worker needed to manage some data in a tree. To do that, they wrote this Java function:

private static boolean existsFather(ArrayList<Integer> fatherFolder, Integer fatherId) { for (Integer father : fatherFolder) { if (father.equals(fatherId)) return true; } return false; }

I do not know what the integers in use represent here. I don't think they're actually representing "folders", despite the variable names in the code. I certainly hope it's not representing files and folders, because that implies they're tossing around file handles in some C-brained approach (but badly, since it implies they've got an open handle for every object).

The core WTF, in my opinion, is this- the code clearly implies some sort of tree structure, the tree contains integers, but they're not using any of the Java structures for handling trees, and implementing this slipshod approach. And even then, this code could be made more generic, as the general process works with any sane Java type.

But there's also the obvious WTF: the java.util.Collection interface, which an ArrayList implements, already handles all of this in its contains method. This entire function could be replaced with fatherFolder.contains(fatherId).

Paul writes: "I guess the last developer didn't know that every implementation of a java.util.Collection has a method called contains. At least they knew how to do a for-each.".

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.
Categories: Computer

Identified the Problem

Mon, 2025-01-27 07:30

Denise's company formed a new team. They had a lot of low-quality legacy code, and it had gotten where it was, in terms of quality, because the company had no real policy or procedures which encouraged good code. "If it works, it ships," was basically the motto. They wanted to change that, and the first step was creating a new software team to kick of green-field projects with an eye towards software craftsmanship.

Enter Jack. Jack was the technical lead, and Jack had a vision of good software. This started with banning ORM-generated database models. But it also didn't involve writing raw SQL either- Jack hand-forged their tables with the Visual Table Designer feature of SQL Server Management Studio.

"The advantage," he happily explained to Denise, "is that we can then just generate our ORM layer right off the database. And when the database changes, we just regenerate- it's way easier than trying to build migrations."

"Right, but even if we're not using ORM migrations, we still want to write migration scripts for our changes to our database. We need to version control them and test them."

"We test them by making the change and running the test suite," Jack said.

And what a test suite it was. There was 100% test coverage. There was test coverage on simple getter/setter methods. There was test coverage on the data transfer objects, which had no methods but getters and setters. There were unit tests for functions that did nothing more than dispatch to built-in functions. Many of the tests just verified that a result was returned, but never checked what the result was. There were unit tests on the auto-generated ORM objects.

The last one, of course, meant that any time they changed the database, there was a significant risk that the test suite would fail on code that they hadn't written. Not only did they need to update the code consuming the data, the tests on that code, they also had to update the tests on the autogenerated code.

Jack's magnum opus, in the whole thing, was that he designed the software with a plugin architecture. Instead of tightly coupling different implementations of various modules together, there was a plugin loader which could fetch an assembly at runtime and use that. Unfortunately, while the whole thing could have plugins, all of the abstractions leaked across module boundaries so you couldn't reasonably swap out plugins without rewriting the entire application. Instead of making a modular architecture, Jack just made starting the application wildly inefficient.

Denise and her team brought their concerns to management. Conversations were had, and it fell upon Jack to school them all. Cheerfully, he said: "Look, not everyone gets software craftsmanship, so I'm going to implement a new feature as sort of a reference implementation. If you follow the pattern I lay out, you'll have an easy time building good code!"

The new feature was an identity verification system which called for end users to upload photographs of their IDs- drivers' licenses, passports, etc. It was not a feature which should have had one developer driving the whole thing, and Jack was not implementing the entire lifecycle of data management for this; instead he was just implementing the upload feature.

Jack pushed it through, out and up into production. Somehow, he short-cut past any code reviews, feature reviews, or getting anyone else to test it. He went straight to a demo in production, where he uploaded his passport and license. "So, there you go, a reference implementation for you all."

Denise went ahead and ran her own test, with a synthetic ID for a test user, which didn't contain any real humans' information. The file upload crashed. In fact, in an ultimate variation of "it works on my machine," the only person who ever successfully used the upload feature was Jack. Of course, since the upload never worked, none of the other features, like retention policies, ever got implemented either.

Now, this didn't mean the company couldn't do identity verification- they had an existing system, so they just kept redirecting users to that, instead of the new version, which didn't work.

Jack went on to other features, though, because he was a clever craftsman and needed to bring his wisdom to the rest of their project. So the file upload just languished, never getting fixed. Somehow, this wasn't Jack's fault, management didn't hold him responsible, and everyone was still expected to follow the patterns he used in designing the feature to guide their own work.

Until, one day, the system was breached by hackers. This, surprisingly, had nothing to do with Jack's choices- one of the admins got phished. This meant that the company needed to send out an announcement, informing users that they were breached. "We deeply regret the breach in our identity verification system, but can confirm that no personal data for any of our customers was affected."

Jack, of course, was not a customer, so he got a private disclosure that his passport and ID had been compromised.

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

Error'd: Office Politics

Fri, 2025-01-24 07:30

"Math is hard, especially timely math," explains The Beast in Black.

 

Cinephile Jono enjoys contemporary dramas far more than sci-fi. "Letterboxd tells me I've logged this movie 3 times, I'm not sure I'll be watching it in 2566." I hope you are, Jono.

 

Pieter may have to turn in his official pedants card. Pieter is concerned about the apparent contradiction between the two salaries on offer here, declaring:
"The title says She can earn up to 8.000 euro/month. But by the time you get to the actual article link, that amount deflated to 6.000 euro/month. I didn't know it was that bad in the euro zone! Or is it good, since we've got inflation under control? I don't know which way is up anymore..."
Pieter, 6,000 is "up to" 8,000. Technically.

 

Cross-country runner Andrei looks to be taking the long way around for this trip. Says he, "According to Google Maps, one of my transfers involves teleporting about 70 km away to catch my next train."

 

Finally David B. reports "My wife works as a quality auditor for Initech [ed: the William Lumberg company, presumably, not the Flavor and Fragrance company]. When setting up contact information for a company she will be auditing in the near future they needed some basic information. So much for gender equality." They're just getting ahead of the next batch of executive orders.

 

[Advertisement] Plan Your .NET 9 Migration with Confidence
Your journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
Categories: Computer

CodeSOD: Stripped of Magic

Thu, 2025-01-23 07:30

A Representative Line is a short snippet that makes you think, "wow, I'd hate to see the rest of the code." A CodeSOD is a longer snippet, which also frequently makes you think, "wow, I'd hate to see the rest of the code," but also is bad in ways that require you to look at the relationship between the lines in the code.

I bring that up, because today's code sample is a long section, but really, it's just a collection of representative lines. Each line in this just makes me die a little on the inside.

Belda found this:

## helper functions function stripmagic($x) { return get_magic_quotes_gpc() ? stripslashes($x) : $x; } function pre_r(&$x) { return '<pre>'.PHSC(print_r($x, true)).'</pre>'; } function PSS($x) { return str_replace('\\"','"',$x); } function PVS($x) { return preg_replace("/\n[^\\S\n]*(?=\n)/", "\n<:vspace>", $x); } function PVSE($x) { return PVS(PHSC($x, ENT_NOQUOTES)); } function PZZ($x,$y='') { return ''; } function PRR($x=NULL) { if ($x || is_null($x)) $GLOBALS['RedoMarkupLine']++; return $x; } function PUE($x) { return preg_replace('/[\\x80-\\xff \'"<>]/e', "'%'.dechex(ord('$0'))", $x); } function SDV(&$v,$x) { if (!isset($v)) $v=$x; } function SDVA(&$var,$val) { foreach($val as $k=>$v) if (!isset($var[$k])) $var[$k]=$v; }

This collection of one-line "helper" functions has it all. Cryptic function names. PRR mutates global variables. PZZ just… returns an empty string. I don't know what PHSC does, and I don't want to, but it's called inside of pre_r and PVSE. Which I also don't know what they do. Speaking of PVSE, I note the regex is using backreferences, which is some advanced regex but I still have no idea what it's doing. I could figure it out, but I don't want to. PUE looks like it might be handling some misencoded characters, maybe. SDV is maybe kinda a coalesce function.

Each line is its own representative line. None of this is code I'd want to maintain.

On the flip side, the abbreviated function names, when read in reverse order, are exactly the sounds I made when I read this code: "SDVASDVPUEPRRPZZPVSEPVSPSS".

It's okay, the doctors say I'll make a full recovery.

[Advertisement] Plan Your .NET 9 Migration with Confidence
Your journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
Categories: Computer

CodeSOD: The 5-Digit Session Identifier

Wed, 2025-01-22 07:30

Sawyer was talking with a co-worker about how their unique session IDs got created. The concern was that they were only five characters long, which meant there could easily be collisions.

They started by looking at the random number generation function.

Public Function RandomNumberGenerator(ByVal min As Integer, ByVal max As Integer, Optional ByVal numDecimals As Integer = 0) As String '*** the generates a number as a string Dim strNum As New StringBuilder Dim rnd As New System.Random Dim i, x, n As Integer Try i = rnd.Next(min, max) If numDecimals > 0 Then Try strNum.Append("9", numDecimals) n = CType(strNum.ToString, Int32) x = rnd.Next(0, n) Catch ex As Exception x = 1 End Try End If strNum.Remove(0, strNum.Length) strNum.Append(i.ToString()) If numDecimals > 0 Then strNum.Append(".") If numDecimals > 99 Then numDecimals = 99 End If strNum.Append(x.ToString("D" & numDecimals.ToString())) End If Return strNum.ToString Catch Return "1.00" End Try End Function

You always know it's going to be bad when you see the random number generator returns a string.

If numDecimals is zero, the code is bad, but vaguely sane. Generate a random number using the built in functions, then return it- as a string.

It's the use of numDecimals which makes this weird. We start by appending "9"s to our string builder, converting it to an integer, and then generating a random number from zero to whatever number of nines we're using. This is the code of someone who hates and fears logarithms.

Then we clear out our string builder because we're starting over with the actual number. Then we append a ".", then we append our number, formatted with our number of decimals string, which we force to be no larger than 99. And this is where we get the special kind of weird.

When we're generating our random decimal number, we do this: strNum.Append("9", numDecimals). This is going to put numDecimals 9s on the string. E.g., if numDecimals is 9, this would set strNum to be 999999999. Thus, when we generate a random number, we generate one between 0 and 99999999.

But, when we append that formatted value to the string, we do this:

If numDecimals > 99 Then numDecimals = 99 End If strNum.Append(x.ToString("D" & numDecimals.ToString()))

Here, we're treating numDecimals as a format string. We're only ever going to output two digits.

The only good news is that while this random function was used everywhere, it wasn't used to generate their random IDs. The bad news, this is how their random IDs.

Public Function RandomQueryStringGenerator() As String '*** the generates an alpha-numeric string 5 digits long such as aa7bb Dim strPwd As New StringBuilder Dim rnd As New System.Random Dim i As Integer Try For x As Integer = 1 To 5 Select Case x Case 1, 2, 4, 5, 8, 9 i = rnd.Next(97, 122) If i Mod 2 = 0 Then strPwd.Append(Chr(i).ToString().ToUpper()) Else strPwd.Append(Chr(i).ToString()) End If Case Else i = rnd.Next(0, 9) strPwd.Append(i.ToString()) End Select Next x Return strPwd.ToString() Catch Return String.Empty End Try End Function [Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!
Categories: Computer

Editor's Soapbox: Ticking Toks and Expertise

Tue, 2025-01-21 07:30

Knowing the kinds of readers we have here, I strongly suspect that if you drew a Venn diagram of "TDWTF Readers" and "TikTok Users" those circles wouldn't overlap at all. But TikTok is in the news, and because my partner uses TikTok, I'm getting second hand smoke of all of this, I think there's some interesting things to talk about here.

If you've been avoiding this news, good for you. For a long recap, Ars can bring up up to date.. But as a quick recap: TikTok is owned by Bytedance, which is based in China, and subject to Chinese laws. TikTok, like every other social media company, is basically spyware, tracking your behavior to sell your eyeballs to advertisers. Over the past few years, all three branches of the US government have decided that the "Chinese ownership" is the problem here (not so much the spying), and passed a law to ban it unless a US company buys it. The whole thing has turned into an idiotic political football, with Biden saying that his waning days of the Presidency wouldn't enforce the ban anyway, and then the whole thing turns into a Trumpist political football as the incoming President is playing Calvinball and making decrees that he did not (at the time) have any authority to make in the first place.

Because of this ban, TikTok ceased operating in the US on Saturday night, displaying banners discussing the ban, and appeals directly to Trump to undo it. On Sunday, TikTok came back up, now with banners thanking Trump for being ready to work with them.

Now, I'm mostly not interested in commenting on the political aspects of this, and you're mostly not interested in hearing it. But for the record: this whole thing is stupid. The root cause of the problem is that the US has no real consumer privacy law, but fixing that problem would be bad for companies like Google and Meta. So, instead, we play Whac-a-Mole with apps, dressing up Sinophobia as a national security threat, and we dance around the 1st Amendment issues. And then the whole thing of a President just deciding to rewrite a law at his whim is terrifying if you like the world to operate according to predictable rules, and presages a rather awful next four years.

What I really want to talk about is conspiracy theories. Because when TikTok came back up, it was suddenly flooded with "IT professionals" who were positing a dark conspiracy: during the downtime, Meta purchased TikTok and migrated all of TikTok's services into Meta's infrastructure. That 12-15 hours of downtime was just the right amount of time to do that switcheroo.

Now, I'm not going to link to any of these videos, because a) as stated, I don't use TikTok, b) TikTok requires you to use the app to watch videos, so screw that, and c) these people don't deserve more views. So there's an element of "take my word for it that this is happening," but also bear with me- because this isn't really what this article is about.

Now, I am not a Site Reliability Engineer, and have no interest in being one. But I've worked with large retailers building global backends for point-of-sale systems where they're handling every point-of-sale transaction in the world. So I have some thoughts about the idea that migrating billions of videos and hundreds of millions of user accounts over to Meta's cloud can be done in 12-15 hours of downtime.

Which, for the record, TikTok mostly uses Oracle's cloud, so add that to the "I Hate Oracle Club" scorecard.

Let's assume Meta purchased TikTok. Would it have needed to spend 12-15 hours down so that Meta could migrate TikTok to their datacenter? Of course not. What an absurd thing to say. As this (Instagram) video rightfully points out, the only people taking down a website for a migration are a dentist office in Ohio in 2007. TikTok is a social media service handling hundreds of millions of users and billions of requests- they're already distributed across multiple datacenters. While spinning up services on a new datacenter isn't a simple task, it's a task that they've certainly already built the tools for. As part of their demand management system, they simply have to have the ability to spin up new service instances quickly and easily- at the scale they operate, that's the only option.

They're a massive distributed system. Adding new infrastructure nodes and mirroring your data elsewhere is a solved problem. All it really takes is time and the budget to run more infrastructure than you need to service requests during the migration.

The real costs are that if you're running in a cloud, you're likely not just using it as a set of virtual private servers- you're likely using your host's infrastructure-as-a-service abstractions, and that means that you might be tightly coupled to their implementation of a variety of cloud services. The real costs are that you'd need to make code changes to actually support a new cloud provider. And that's definitely not happening in a 12-15 hour time frame.

But this is a dumb conversation to have, because if we assume Meta bought TikTok: there's no need to migrate anywhere. In this scenario, Meta has the keys to TikTok's infrastructure. Whatever they want to do, they can just… do. Sure, it means paying Oracle for hosting, but TikTok is making money. It's a net win. Over the next months or even years, Meta could move TikTok services into their private cloud and perhaps save costs, but there's no need to migrate on a tight timeline.

Okay, so with all that said, what an idiot I am, right? Here I am, arguing against people I don't know, who definitely aren't going to read this. I don't even like TikTok, and think every social media app is a step down from just plain old RSS feeds, because I am an old person. We're deep into "someone is wrong on the Internet" territory. So why did this drive me up onto the soapbox?

Because hearing all this conspiracy mongering nonsense reminds me of an important truth: everything looks like a conspiracy when you don't know how anything works.

If you don't know how cloud deployments work, TikTok's downtime can look like a conspiracy. If you don't know how election systems are designed, any electoral result you don't like can look a lot like a conspiracy. If you don't know how the immune system works, vaccines can look like a conspiracy. If you don't know how anything works, a flat Earth starts making sense.

Realistically, no one of us can know how everything works. In an ideal world, we can defer to experts who do know how things work. But to defer to experts, we need to trust expertise.

And as a society, trust in experts has been eroding. A lot of it comes from propagandists who want their ignorance to be valued at least as highly as expertise. Being loudly wrong about things is a great way to get attention and with that, money. Alex Jones made many millions being loudly wrong.

But it's not just loudmouthed morons that are eroding the trust in experts. Experts can and have abused the public trust. The poster child for "Worst Person Ever" is Thomas Midgely, Jr., who lied to the public and created a gigantic public health disaster, then went on to create a gigantic environmental disaster (in his defense, CFCs destroying the ozone layer wasn't something he knew about, but he absolutely knew about the dangers of leaded gasoline).

And even more than that, in a society where peoples' prospects look worse with each passing year, with entire generations deciding that buying a home and having children are just going to be out of reach, we have to ask: what good is it to listen to experts if it doesn't lead to good outcomes? When all the experts work for a big mega corporation and put their big brains to work figuring out how to turn my eyeballs into dollars, or are working for think tanks and government agencies captured by those corporations, what good are experts?

All in all, it looks bleak. There's no easy fix for any of this. The systems which make expertise viable have eroded over the past decades, taken for granted. Public trust in just… everything has weakened. Fixing this requires broad social changes. A minor tech blog that focuses in the ways people screw things up is not going to drive broad social changes.

But I think there's one thing I can drive from here, and it comes back to this one simple statement: everything looks like a conspiracy when you don't know how anything works.

So, I'm going to put out this call: when you know how things work, share that. Share what you know! Share it on social media. Share it on your own personal blog. Share it in local meeting groups. Hell, share it on TikTok, because gods know, they need it.

But also don't forget the flip side: when you don't know, be careful about finding conspiracies. When you don't know how something works, it might look like a conspiracy. But, frequently, it's not- you're just ignorant. And honestly, we should be as open about our ignorance as we are about our knowledge. We should know what we don't know, or at least know when we're stepping outside of our areas of confidence.

So let me close with this: do you have a place you're sharing the things you know? Do you think it'd be of interest to our readers? Use our submission form, and use the subject/title "Reader Link". If I get enough interesting links, I may do a roundup of them.

Tomorrow, we'll return to our regularly scheduled programming.

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

CodeSOD: Consultant Conversions

Mon, 2025-01-20 07:30

Janet's company had a glut of work, and thus didn't have the staffing required to do it all. It didn't make sense to hire on any new full-time employees, so they went the route of bringing on a few highly paid consultants, specifically ones who specialized in one specific problem: talking to a piece of hardware purchased from a vendor.

The hardware in question was a scientific which communicated over a serial line. This device provided a lot of data that represented decimal values, but that data was not encoded as an IEEE float. Instead, they used two integers- one for the data, and one representing the number of decimal places.

So, for example, "555.55" would be represented as "55555 2".

Now, in embedded devices, this isn't too unusual. It's entirely possible that the embedded CPU didn't even support true floating point operations, and this was just how they decided to work around that.

When communicating over the serial line, the device didn't send the data encoded in binary, however- it did everything as text. This was arguably helpful as it meant a technician could communicate with the device directly over a terminal emulator, but it meant any software talking to the device had to parse data.

Which brings us to the code written by the highly paid consultants. This code needs to take two 16-bit integers and turn them into a single decimal value. Let's see how they did it.

/// <summary> /// Sets the single parameter value. /// </summary> /// <param name="Value">Name of the parameter.</param> /// <param name="decimals"></param> /// <returns></returns> public double ConvertIntToDecimal(string Value, string decimalCount) { double Result; var decimals = UInt16.Parse(decimalCount); var Val = UInt16.Parse(Value); if (decimals > 0) { var divider = Math.Pow(10, decimals); Result = ((float)Val) / divider; } else { Result = Val; } return Result; }

We start with comments that are just wrong, which is always a good start. The whole thing has delightfully randomized capitalization- a mix of PascalCase and camelCase.

In the core logic, we parse the input values, and if there are any decimal places, we do some arithmetic to build our floating point value. We get the fun bonus inconsistency of casting to float when we handle our result in double, but at least it's a widening inconsistency, I suppose.

As an overall approach to the problem, it's not a train wreck, but there's one very important thing that our highly paid consultant forgot. Our HPC, remember, was an expert in this particular instrument, or at least that was their claim. And while they're mistake is an easy mistake to make while coding, it should also be an easy mistake to catch during testing, too.

What was the mistake?

The value is frequently negative, and they're using UInt16 to parse the inputs. Which means this function frequently threw an exception. Literally five minutes of testing would have turned it up. Janet had piles of sample data, recorded from the device, which she used for testing. Almost all of her test cases would trigger the bug at some point.

It seems likely, at this juncture, that the HPC simply never actually tested the code. They wrote it. They committed it. They collected their check and left. Janet may have been the first person to actually run the code at all.

In the end, hiring the HPC cost a lot of money, and maybe saved a few days of work over the course of months. It's hard to say, as it may have created more work, since so much of what the HPC did had to be debugged and often rewritten.

The "good" news is that they have another glut of work, so management is looking to bring back the consultants for another round.

[Advertisement] Plan Your .NET 9 Migration with Confidence
Your journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
Categories: Computer

Error'd: Secret Horror

Fri, 2025-01-17 07:30

Casanova Matt swings for the fences. "OKCupid (they don't capitalize the K, but I do, for propriety) must have migrated their match questions through Excel during a recent site revamp. These answers should obviously be 1-2 and 3-4, but maybe I could have 2 with Jan and 4 with Margaret (Mar to friends)."

 

Jan B. likes to keep his options open. "I haven't received any emails with a forgotten placeholder in a long, long time, so Apple Intelligence thought it was time to put one in an email summary. The [product name] text itself is not present anywhere in the source of the email or any of the headers (and I've checked the raw source of the email)."

 

Patrick Rottman almost lost his cool at Home Depot this week. "When your $3,300 smart fridge is powered by the same web dev practices as a high school project."

 

Mark found a sneaky security question that has me completely stumped. "The I-don't-care-about-cookies addon also doesn't care about their users (or their system) (I changed the html tag from img to iframe to display this error, otherwise it's just a broken image)"

 

We always like these "lol there's a computer behind this curtain" moments, probably because we're so old that it just seems like of course movies are totally analog right? Apparently so is jeffphi as he was surprised by an unexpected error. I laughed, I cried... "Welp, didn’t expect to see this at the theater tonight! At first, I thought it was the beginning of some weird ad, but it just stayed there way too long. They got it worked out after about three minutes and the trailers began playing. Perhaps the real WTF is that our theater is using WindowsXP?!"

 

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

CodeSOD: Halfway to a Date

Thu, 2025-01-16 07:30

Roger took on a contract to fix up a PHP website. During the negotiations, he asked some questions about the design, like, "Is it object-oriented or more procedural?" "No, it's PHP," said the developer.

Which about sums it up, I suppose. Have some date handling code:

function MnameToMnumber($mname) //takes month name 'January' and returns month number '01' { if($mname == 'January'){$mnum = '01';} elseif($mname == 'February'){$mnum = '02';} elseif($mname == 'March'){$mnum = '03';} elseif($mname == 'April'){$mnum = '04';} elseif($mname == 'May'){$mnum = '05';} elseif($mname == 'June'){$mnum = '06';} elseif($mname == 'July'){$mnum = '07';} elseif($mname == 'August'){$mnum = '08';} elseif($mname == 'September'){$mnum = '09';} elseif($mname == 'October'){$mnum = '10';} elseif($mname == 'November'){$mnum = '11';} elseif($mname == 'December'){$mnum = '12';} return $mnum; } function MnumberToMname($mname) //takes month number '01' and returns month name '01' { if($mnum= '01'){$mname = 'January';} elseif($mnum= '02'){$mname = 'February';} elseif($mnum= '03'){$mname = 'March';} elseif($mname == 'April'){$mnum = '04';} elseif($mnum= '05'){$mname = 'May';} elseif($mnum= '06'){$mname = 'June';} elseif($mnum= '07'){$mname = 'July';} elseif($mnum= '08'){$mname = 'August';} elseif($mnum= '09'){$mname = 'September';} elseif($mnum= '10'){$mname = 'October';} elseif($mnum= '11'){$mname = 'November';} elseif($mnum= '12'){$mname = 'December';} return $mname; }

So, for starters, I "love" the use of Whitesmiths indenting. I don't think I've seen this in the wild. (I predict the comments section will be links to articles where I have seen this in the wild).

Beyond that, there's nothing terribly surprising here, in terms of bad date handling code, with a few small exceptions. First is their insistence on the conversion itself being stringly typed: January isn't month 1, but "01".

But more notable: MnumberToMname just doesn't work. They're using the assignment operator instead of the equality operator. At least, for all the cases where they're doing the correct comparison direction. A stray "name to number" conversion is lurking in April. Not that it matters- this will always return January.

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

CodeSOD: Brushing Up

Wed, 2025-01-15 07:30

Keige inherited some code which seems to be part of a drawing application. It can load brush textures from image files- at least, sometimes it can.

static public Brush GetImageBrush(string serviceCode, string imageName, string language) { Brush BorderChannelGroupBrush; BitmapImage image = null; int point = imageName.LastIndexOf('.'); string languageImagename = imageName.Substring(0, point) + "-" + language + imageName.Substring(point); try { image = FrameWork.ServicePageImageUrlOnContentServer(serviceCode, languageImagename); } catch { } if (image == null) { try { image = FrameWork.ServicePageImageUrlOnContentServer(serviceCode, imageName); } catch { } } if (image != null) { BorderChannelGroupBrush = new ImageBrush(image); } else { BorderChannelGroupBrush = Brushes.White; } return BorderChannelGroupBrush; }

There are a lot of interesting "choices" made in this code. First, there's the old "find the last '.'" approach of grabbing the file extension. Which is fine, but there's a built-in which handles cases like when there isn't an extension better. I think, in this case, it probably doesn't hurt anything.

But the real fun starts with our first attempt at loading our image. We jam a localized language string in the middle of the file name (foo-en.jpg), and try and fetch that from the server. If this fails, it throws an exception… which we ignore.

But we don't fully ignore it! If the exception was thrown, image doesn't get set, so it's still null. So we do a null check, and repeat our empty exception handler. If the image is still null after that, we default to a "Brushes.White" image.

It's all a very awkward and weird way to handle errors. The null checks bring with them the whiff of a C programmer checking return codes, but I don't actually think that's what happened here. I think this was just someone not fully understanding the problem they were trying to solve or the tools available to them. Or maybe they just really didn't want to deal with nesting.

It's hardly the worst code, but it does just leave me feeling weird when I look at it.

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.
Categories: Computer

Representative Line: The Whole Thing

Tue, 2025-01-14 07:30

David was integrating a new extension into their ecommerce solution, and found this un-representative line:

$this->model_sale_manageorder->exportOrder(substr($selectid,0,strlen($selectid)-1));

Note the use of substr- we take the substr of $selectid from 0 to strlen($selectid)- aka, we take the entire string.

Perhaps this is leftover code, where once upon a time there was a prefix or suffix on the string which needed to be ignored. But the result is code that is rather dumb.

I call this an "un-representative line" because, according to David, the rest of the code in the extension was actually rather good. Even otherwise good code is not immune to having a little fart hiding under the covers, I suppose.

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.
Categories: Computer

CodeSOD: Irritants Make Perls

Mon, 2025-01-13 07:30

Grün works for a contracting company. It's always been a small shop, but a recent glut of contracts meant that they needed to staff up. Lars, the boss, wanted more staff, but didn't want to increase the amount paid in salaries any more than absolutely necessary, so he found a "clever" solution. He hired college students, part time, and then threw them in the deep end of Perl code, a language some of them had heard of, but none of them had used.

It didn't go great.

# note that $req is immutable (no method apart from constructor sets a value for its members) sub release { my $req = shift; my $body = 'operation:' . ' '; if (uc($req->op()) eq 'RELEASE') { $body .= 'release' . "\n"; # do more stuff to body ... } else { $body = 'operation: request' . "\n"; } if (uc($req->op()) ne 'RELEASE') { register_error('unable to send release mail'); } # and so on ... }

This method checks a $req parameter. Notably, it's not being passed as a prototype parameter, e.g. as part of the signature- sub release($req)- but accessed by shifting out of @_, the special variable which holds all the parameters. This is the kind of move that gives Perl it's reputation for being write only, and it's also a sign that they were cribbing off the Perl documentation as they write. For whatever reason, using shift seems to be the first way Perl documentation teaches people to write subroutines.

This whole thing is doing string concatenation on a $body variable, presumably an email body. I'd normally have unkind words here, but this is Perl- giant piles of string concatenation is just basically par for the course.

The "fun" part in this, of course, is the if statements. If the $req is to "RELEASE", we append one thing to the body, if it's not, we append a different thing. But if it's not, we also register_error. Why couldn't that be in the else block? Likely because the poor developers didn't have a good understanding of the code, and the requirements kept changing. But it's a little head scratcher, especially when we look at the one place this function is called:

if (uc($req->op()) eq 'RELEASE') { return release($req); }

Now, on one hand, having the function check for its error condition and avoiding triggering the error condition at the call site is good defensive programming. But on the other, this all sorta smacks of a developer not fully understanding the problem and spamming checks in there to try and prevent a bug from appearing.

But the real fun one is this snippet, which seems like another case of not really understanding what's happening:

if(($ok1==1 and $ok3==1)or($ok1==1 and $ok3==1)) { print p("Master changed!"); }

We just check the same condition twice.

Now, of course, it's not the developers' fault that they didn't have a good picture of what they should have been doing. Lars was trying to save money by hiring the inexperienced, and as usually happens, the entire thing cost him more money, because Grün and the rest of the team needed to go back over the code and rewrite it.

The upshot, for our college students, is that this was a good resume builder. They've all since moved on to bigger companies with better paychecks and actual mentoring programs that will develop their skills.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
Categories: Computer

Error'd: Not Impossible

Fri, 2025-01-10 07:30

Someone online said we run a Mickey Mouse outfit. Angered beyond words, we consulted legal@disney.com and they threatened to find that guy and sue him. So to anyone else who thinks this column is Goofy, you should know that the world's definitive authorities insist that it absolutely is not.

But these guys? This website actually is kind of goofy, according to resolutioner Adam R. who crowed "Someone forgot to localize some text for the new year!"

 

Fellow resolutioner Brian says he "decided to learn some new skills for the new year, and this came up as part of the introductory lesson. Tip #1: pick a competent vendor."

 

"The unread email count was unusually high for a Sunday," noted an anonymous reader. Is "email inflation" a thing?

 

Greek Paul K. comes bearing this gift "Oh sure, lets leave debug on production, what's the worst it can happen? Greek museum fail."

 

"Maths is hard," muses Matthew S. "but the easy solution is just to make 42 be the answer to everything!"

 

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
Categories: Computer

CodeSOD: Crossly Joined

Thu, 2025-01-09 07:30

Antonio's team hired some very expensive contractors and consultants to help them build a Java based application. These contractors were very demure, very mindful, about how using ORMs could kill performance.

So they implemented a tool that would let them know any time the Hibernate query generator attempted to perform a cross join.

public class DB2390Dialect extends org.hibernate.dialect.DB2390Dialect { private Logger logger = LoggerFactory.getLogger(DB2390Dialect.class); @Override public String getCrossJoinSeparator() { try { Exception e = new Exception(); throw e; } catch (Exception xe) { logger.warn("cross join ", xe.getMessage()); } return ", "; } }

I'm going to call this one a near miss. I understand what they were trying to do.

Hibernate uses a set of "dialect"s to convert logical operations in a query to literal syntax- as you can see here, this function turns a cross join operation into a ", ".

What they wanted to do was detect where in the code this happened and log a message. They wanted the message to contain a stack trace, and that's why they threw an exception. Unfortunately, they logged, not the stack trace, but the message- a message which they're not actually setting. Thus, the logger would only ever log "cross join ", but with no information to track down when or why it happened.

That said, the standard way in Java of getting the stack trace skips the exception throwing: StackTraceElement[] st = new Throwable().getStackTrace(). Of course, that would have made them do some actual logging logic, and not just "I dunno, drop the message in the output?"

The only remaining question is how much did this feature cost? Since these were "expert consultants", we can ballpark it as somewhere between "a few thousand dollars" to "many thousands of dollars"..

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

CodeSOD: My Identification

Wed, 2025-01-08 07:30

Bejamin's team needed to generate a unique session ID value that can't easily be guessed. The traditional way of doing this would be to generate cryptographically secure random bytes. Most languages, including PHP, have a solution for doing that.

But you could also do this:

protected function _createId() { $id = 0; while (strlen($id) < 32) { $id .= mt_rand(0, mt_getrandmax()); } $id = md5(uniqid($id, true)); return $id; }

Now, mt_rand is not cryptographically secure. They generate a random number (of arbitrary size) and concatenate it to a string. When the string is 32 characters long (including a leading zero), we call that enough.

This is not generating random bytes. To the contrary, the bytes it's generating are very not random, seeing as they're constrained to a character between 0 and 9.

We then pass that through the uniqid function. Now, uniqid also generates a non-cryptographically secure unique identifier. Here, we're specifying our large number is the prefix to that unique ID, and asking for more randomness to be added (the true parameter). This is better than what they did with the while loop above, though still not the "correct" way to do it.

Finally, we pass it through the md5 algorithm to reduce it to a hash, because we just love hash collisions.

It's impressive that, given a chance to make a choice about security-related features, they were able to make every single wrong choice.

This is also why you don't implement this stuff yourself. There are far more ways to get it wrong than there are ways to get it right.

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.
Categories: Computer

Representative Line: Generate JSON

Tue, 2025-01-07 07:30

Today's anonymous submission is a delightfully simple line of JavaScript which really is an archetype of a representative line.

$json = "{";

Now, I know you're thinking, "I see a '$' sigil, this must be PHP or maybe Perl!" No, this is JavaScript. And as you might be gathering from the code, this is the first line in a long block that constructs JSON through string concatenation.

And yes, JavaScript has built in functions for this, which work better than this. While it's possible that they need to generate custom JSON to support a misbehaving parser on the other side, that's it's own WTF- and it isn't the case here. The developers responsible simply didn't know how to handle JSON in JavaScript.

Do you know what else they couldn't understand? Source control and collaboration tools, so all of the JavaScript files were named things like david.js and lisa.js- each developer got their own JS file to work on, so they didn't conflict with anyone else.

[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.
Categories: Computer

CodeSOD: Mr Number

Mon, 2025-01-06 07:30

Ted's company hired a contract team to build an application. The budget eventually ran out without a finished application, so the code the contract team had produced was handed off to Ted's team to finish.

This is an example of the Ruby code Ted inherited:

def self.is_uniqueness(mr_number) out = false mrn = PatientMrn.find_by_mr_number(mr_number) if mrn out = true return mrn end return nil end

The function is called is_uniqueness which is definitely some language barrier naming (is_unique is a more English way of wording it). But if we trace through the logic, this is just a wrapper around PatientMrn.find_by_mr_number- it returns an "mrn".

So, the first obvious problem: this isn't checking uniqueness in any way, shape or form.

Then there's the whole check for a valid record- either we find a record or we return nil. But since find_by_mr_number is clearly returning something falsy, that doesn't seem necessary.

And that is the default behavior for the Rails generated find_by methods- they just return nil if there are no records. So none of the checks are needed here. This whole method isn't needed here.

Finally, there's out. I have no idea what they were trying to accomplish here, but it smells like they wanted a global variable that they could check after the call for error statuses. If that was their goal, they failed in a few ways- first, returning nil conveys the same information. Second, global variables in Ruby get a $ sigil in front of them.

What the out variable truly represents is "do I want out of this codebase?" True. That is definitely true.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
Categories: Computer

Error'd: Monkeys

Fri, 2025-01-03 07:30

Happy 2025 to all our readers. I can already tell this year's columns are going to be filled with my (least) favorite form of WTF, the impossible endless gauntlet of flaming password hurdles to jump over or crawl under. Please comment if you know why this week's column has this title and why it doesn't have the title Swordfish.

Peter G. starts off our new year of password maladies with a complaint that is almost poetic.
"Between desire and reality.
Between fact and breakfast.
Between 8 and -6:00.
Madness lies, lies, lies..."

 

Rick P. keeps it going. "Must begin with a capital letter!!!" he exclaims with three (3!) exclamation points. I think it deserved four.

 

Mark Whybird justifiably grumbled "If I knew what the policy was, maybe then I could come up with a password to satisfy it!"

 

R3D3-1 who swears he is NOT a bot, found a kind of interestingly complicated error challenge, opining "Password requirements can be annoying. Especially when they don't tell you what the requirements are, only that you failed. But here they send me a confirmation Email, and only when clicking the confirmation Email did they even tell me! That's kind of a new low there... "

 

And finally ending this beginning, faithful Pascal remarks only: "WTF". Well said.

 

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.
Categories: Computer

CodeSOD: intint

Thu, 2025-01-02 07:30

Ash's company outsourced to an offshore vendor.

This is an example of what they got back:

public System.Data.DataSet SetQuotaCellTotal(string sessionId, ArrayOfKeyValueOfintintKeyValueOfintint[] cellData, bool modifyTotalSample)

The notable bit in this code is the type ArrayOfKeyValueOfintintKeyValueOfIntint. We're in the world of InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState in terms of names, but this one has the added bonus of being misleading.

This type represents a key/value pair. They're both integers. That's it. It's a pair of ints. As a bonus, they're passing an array of key value pairs into the function- an array of ArrayOfKeyValueOfintintKeyValueOfintint. That's a mouthful.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
Categories: Computer

Pages