The Daily WTF

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

Making a Pass of Yourself

Mon, 2025-06-02 08:30

Frederico planned to celebrate the new year with friends at the exotic international tourist haven of Molvania. When visiting the area, one could buy and use a MolvaPass (The Most Passive Way About Town!) for free or discounted access to cultural sites, public transit, and more. MolvaPasses were available for 3, 7, or 365 days, and could be bought in advance and activated later.

Still outside the country the week before his trip, Frederico had the convenience of buying a pass either online or via an app. He elected to use the website, sitting down before his home PC and entering the address into his web browser. Despite his fiber internet connection, he sat on a white screen for several seconds while the GoMolva Tourist Board website loaded. He then clicked the obvious Buy Now button in the top-right corner. After several more seconds, he was presented with a page requiring him to create an account.

Frederico did so, specifying his email address and a 16-character password suggested by Bitwarden. He then received a confirmation link in his email inbox. Upon clicking that, he was presented with an interface where he could add MolvaPasses to a shopping cart. He selected one 3-day pass and paid with PayPal. The website redirected him to the proper screen; he entered his PayPal credentials and confirmed the payment.

From there, he was redirected to a completely white screen. After waiting several seconds, a minute ... nothing changed. PayPal sent him a receipt, but there was no confirmation from the GoMolva Tourist Board website.

Frederico decided to refresh the page. This time, he saw the default Apache screen on CentOS.

His jaw almost hit the floor. They were still using CentOS, despite the fact that it'd been abandoned? Horrified, he bailed on that tab, desperately opening a fresh one and manually entering the URL again.

Finally, the page loaded successfully. Frederico was still logged in. From there, he browsed to the My Passes section. His 3-day MolvaPass was there, listed as Not activated.

This was exactly what Frederico had hoped he would see. With a sigh of relief, he turned his attention away from his laptop to his phone. For the sake of convenience, he wanted to download the MolvaPass app onto his phone. Upon doing so, he opened it and entered his username and password on the initial screen. After clicking Login, the following message appeared: The maximum length of the password is 15 characters.

Frederico's blood froze. How was that possible? There'd been no errors or warnings when he'd created his login. Everything had been fine then. Heart pounding, Frederico tried logging in again. The same error appeared. He switched back to his computer, where the site was still open. He browsed to My Account and selected Change Password.

A new screen prompted him for the old password, and a new one twice. He hurriedly filled in the fields and clikced the Change Password button.

A message appeared: Your MolvaPass has been successfully activated.

"What?!" Frederico blurted out loud. There was nothing to click but an OK button.

A follow-up message assured him, Password has been successfully changed.

As terror bolted down his spine, an expletive flew from his mouth. He navigated back to My Passes. There beside his newly-purchased pass was the big green word Activated.

"I only changed the password!" he pleaded out loud to a god who clearly wasn't listening. He forced a deep breath upon his panicked self and deliberated what to do from there. Support. Was there any way to get in touch with someone who could undo the activation or refund his money? With some Googling, Frederico found a toll-free number he could call from abroad. After he rapidly punched the number into his phone, a stilted robot voice guided him through a phone menu to the "Support" option.

We're getting somewhere, Frederico reassured himself.

"FoR MoLvaPaSs suPpOrt, uSe ThE cOnTaCt FoRm oN tHe GoMoLvA WeBzOnE." The robot hung up.

Frederico somehow refrained from hurling his phone across the room. Turning back to his PC, he scrolled down to the website footer, where he found a Contact us link. On this page, there was a contact form and an email address. Frederico filled out the contact form in detail and clicked the Submit button.

A new message appeared: Unable to send the request, try again later.

Frederico rolled his eyes toward the heavens. Somehow, he managed to wait a good five minutes before trying again—in vain. Desperately, he took his detailed message and emailed it to the support address, hoping for a quick response.

Minutes crawled past. Hours. Nothing by the time Frederico went to bed. It wasn't until the next morning that a response came back. The entire message read: The MolvaPass should have been activated once you reached Molvania, not before.

Consumed with soul-burning fury, Frederico hit Caps Lock on his keyboard. MAYBE MY PREVIOUS EMAIL WAS TOO LONG OR DIFFICULT TO UNDERSTAND?? ALL I DID WAS CHANGE THE PASSWORD!!!!

Several hours later, the following reply: The change of pw is not related to the activation of the pass.

Frederico directed his rage toward escalating the matter. He managed to track down the company that'd built the GoMolva website, writing to their support to demand a cancellation of the MolvaPass and a full refund. A few hours later, their reply asked for his PayPal transaction code so they could process the request.

In the end, Frederico got his money back and resolved to wait until he was physically in Molvania before attempting to buy another MolvaPass. We can only hope he rang in the new year with sanity intact.

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

Error'd: Lucky Penny

Fri, 2025-05-30 08:30

High-roller Matthew D. fears Finance. "This is from our corporate expense system. Will they flag my expenses in the April-December quarter as too high? And do we really need a search function for a list of 12 items?"

 

Tightfisted Adam R. begrudges a trifling sum. "The tipping culture is getting out of hand. After I chose 'Custom Tip' for some takeout, they filled out the default tip with a few extra femtocents. What a rip!"

 

Cool Customer Reinier B. sums this up: "I got some free B&J icecream a while back. Since one of them was priced at €0.01, the other one obviously had to cost zero point minus 1 euros to make a total of zero euro. Makes sense. Or probably not."

 

An anonymous browniedad is ready to pack his poptart off for the summer. "I know {First Name} is really excited for camp..." Kudos on getting Mom to agree to that name choice!

 

Finally, another anonymous assembler's retrospective visualisation. "CoPilot rendering a graphical answer of the semantics of a pointer. Point taken. " There's no error'd here really, but I'm wondering how long before this kind of wtf illustration lands somewhere "serious".

 

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

CodeSOD: Recasting the Team

Thu, 2025-05-29 08:30

Nina's team has a new developer on the team. They're not a junior developer, though Nina wishes they could replace this developer with a junior. Inexperience is better than whatever this Java code is.

Object[] test = (Object[]) options; List<SchedulePlatform> schedulePlatformList = (List<SchedulePlatform>)((Object[])options)[0]; List<TableColumn> visibleTableCols = (List<TableColumn>)((Object[])options)[1];

We start by casting options into an array of Objects. That's already a code stench, but we actually don't even use the test variable and instead just redo the cast multiple times.

But worse than that, we cast to an array of object, access an element, and then cast that element to a collection type. I do not know what is in the options variable, but based on how it gets used, I don't like it. What it seems to be is a class (holding different options as fields) rendered as an array (holding different options as elements).

The new developer (ab)uses this pattern everywhere.

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

CodeSOD: Format Identified

Wed, 2025-05-28 08:30

Many nations have some form of national identification number, especially around taxes. Argentina is no exception.

Their "CUIT" (Clave Única de Identificación Tributaria) and "CUIL" (Código Único de Identificación Laboral) are formatted as "##-########-#".

Now, as datasets often don't store things in their canonical representation, Nick's co-worker was given a task: "given a list of numbers, reformat them to look like CUIT/CUIL. That co-worker went off for five days, and produced this Java function.

public String normalizarCuitCuil(String cuitCuilOrigen){ String valorNormalizado = new String(); if (cuitCuilOrigen == null || "".equals(cuitCuilOrigen) || cuitCuilOrigen.length() < MINIMA_CANTIDAD_ACEPTADA_DE_CARACTERES_PARA_NORMALIZAR){ valorNormalizado = ""; }else{ StringBuilder numerosDelCuitCuil = new StringBuilder(13); cuitCuilOrigen = cuitCuilOrigen.trim(); // Se obtienen solo los números: Matcher buscadorDePatron = patternNumeros.matcher(cuitCuilOrigen); while (buscadorDePatron.find()){ numerosDelCuitCuil.append(buscadorDePatron.group()); } // Se le agregan los guiones: valorNormalizado = numerosDelCuitCuil.toString().substring(0,2) + "-" + numerosDelCuitCuil.toString().substring(2,numerosDelCuitCuil.toString().length()-1) + "-" + numerosDelCuitCuil.toString().substring(numerosDelCuitCuil.toString().length()-1, numerosDelCuitCuil.toString().length()); } return valorNormalizado; }

We start with a basic sanity check that the string exists and is long enough. If it isn't, we return an empty string, which already annoys me, because an empty result is not a good way to communicate "I failed to parse".

But assuming we have data, we construct a string builder and trim whitespace. And already we have a problem: we already validated that the string was long enough, but if the string contained more trailing whitespace than a newline, we're looking at a problem. Now, maybe we can assume the data is good, but the next line implies that we can't rely on that- they create a regex matcher to identify numeric values, and for each numeric value they find, they append it to our StringBuilder. This implies that the string may contain non-numeric values which need to be rejected, which means our length validation was still wrong.

So either the data is clean and we're overvalidating, or the data is dirty and we're validating in the wrong order.

But all of that's a preamble to a terrible abuse of string builders, where they discard all the advantages of using a StringBuilder by calling toString again and again and again. Now, maybe the function caches results or the compiler can optimize it, but the result is a particularly unreadable blob of slicing code.

Now, this is ugly, but at least it works, assuming the input data is good. It definitely should never pass a code review, but it's not the kind of bad code that leaves one waking up in the middle of the night in a cold sweat.

No, what gets me about this is that it took five days to write. And according to Nick, the responsible developer wasn't just slacking off or going to meetings the whole time, they were at their desk poking at their Java IDE and looking confused for all five days.

And of course, because it took so long to write the feature, management didn't want to waste more time on kicking it back via a code review. So voila: it got forced through and released to production since it passed testing.

.comment { border: none; } [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

The Missing Link of Ignorance

Tue, 2025-05-27 08:30

Our anonymous submitter, whom we'll call Craig, worked for GlobalCon. GlobalCon relied on an offshore team on the other side of the world for adding/removing users from the system, support calls, ticket tracking, and other client services. One day at work, an urgent escalated ticket from Martin, the offshore support team lead, fell into Craig's queue. Seated before his cubicle workstation, Craig opened the ticket right away:

The new GlobalCon support website is not working. Appears to have been taken over by ChatGPT. The entire support team is blocked by this.

Instead of feeling any sense of urgency, Craig snorted out loud from perverse amusement.

"What was that now?" The voice of Nellie, his coworker, wafted over the cubicle wall that separated them.

"Urgent ticket from the offshore team," Craig replied.

"What is it this time?" Nellie couldn't suppress her glee.

"They're dead in the water because the new support page was, quote, taken over by ChatGPT."

Nellie laughed out loud.

"Hey! I know humor is important to surviving this job." A level, more mature voice piped up behind Craig from the cube across from his. It belonged to Dana, his manager. "But it really is urgent if they're all blocked. Do your best to help, escalate to me if you get stuck."

"OK, thanks. I got this," Craig assured her.

He was already 99.999% certain that no part of their web domain had gone down or been conquered by a belligerent AI, or else he would've heard of it by now. To make sure, Craig opened support.globalcon.com in a browser tab: sure enough, it worked. Martin had supplied no further detail, no logs or screenshots or videos, and no steps to reproduce, which was sadly typical of most of these escalations. At a loss, Craig took a screenshot of the webpage, opened the ticket, and posted the following: Everything's fine on this end. If it's still not working for you, let's do a screenshare.

Granted, a screensharing session was less than ideal given the 12-hour time difference. Craig hoped that whatever nefarious shenanigans ChatGPT had allegedly committed were resolved by now.

The next day, Craig received an update. Still not working. The entire team is still blocked. We're too busy to do a screenshare, please resolve ASAP.

Craig checked the website again with both laptop and phone. He had other people visit the website for him, trying different operating systems and web browsers. Every combination worked. Two things mystified him: how was the entire offshore team having this issue, and how were they "too busy" for anything if they were all dead in the water? At a loss, Craig attached an updated screenshot to the ticket and typed out the best CYA response he could muster. The new support website is up and has never experienced any issues. With no further proof or steps to reproduce this, I don't know what to tell you. I think a screensharing session would be the best thing at this point.

The next day, Martin parroted his last message almost word for word, except this time he assented to a screensharing session, suggesting the next morning for himself.

It was deep into the evening when Craig set up his work laptop on his kitchen counter and started a call and session for Martin to join. "OK. Can you show me what you guys are trying to do?"

To his surprise, he watched Martin open up Microsoft Teams first thing. From there, Martin accessed a chat to the entire offshore support team from the CPO of GlobalCon. The message proudly introduced the new support website and outlined the steps for accessing it. One of those steps was to visit support.globalcon.com.

The web address was rendered as blue outlined text, a hyperlink. Craig observed Martin clicking the link. A web browser opened up. Lo and behold, the page that finally appeared was www.chatgpt.com.

Craig blinked with surprise. "Hang on! I'm gonna take over for a second."

Upon taking control of the session, Craig switched back to Teams and accessed the link's details. The link text was correct, but the link destination was ChatGPT. It seemed like a copy/paste error that the CPO had tried to fix, not realizing that they'd needed to do more than simply update the link text.

"This looks like a bad link," Craig said. "It got sent to your entire team. And all of you have been trying to access the support site with this link?"

"Correct," Martin replied.

Craig was glad he couldn't be seen frowning and shaking his head. "Lemme show you what I've been doing. Then you can show everyone else, OK?"

After surrendering control of the session, Craig patiently walked Martin through the steps of opening a web browser, typing support.globalcon.com into the header, and hitting Return. The site opened without any issue. From there, Craig taught Martin how to create a bookmark for it.

"Just click on that from now on, and it'll always take you to the right place," Craig said. "In the future, before you click on any hyperlink, make sure you hover your mouse over it to see where it actually goes. Links can be labeled one thing when they actually take you somewhere else. That's how phishing works."

"Oh," Martin said. "Thanks!"

The call ended on a positive note, but left Craig marveling at the irony of lecturing the tech support lead on Internet 101 in the dead of night.

[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

Classic WTF: Superhero Wanted

Mon, 2025-05-26 08:30
It's a holiday in the US today, so we're taking a long weekend. We flip back to a classic story of a company wanting to fill 15 different positions by hiring only one person. It's okay, Martin handles the database. Original - Remy

A curious email arrived in Phil's Inbox. "Windows Support Engineer required. Must have experience of the following:" and then a long list of Microsoft products.

Phil frowned. The location was convenient; the salary was fine, just the list of software seemed somewhat intimidating. Nevertheless, he replied to the agency saying that he was interested in applying for the position.

A few days later, Phil met Jason, the guy from the recruitment agency, in a hotel foyer. "It's a young, dynamic company", the recruiter explained,"They're growing really fast. They've got tons of funding and their BI Analysis Suite is positioning them to be a leading player in their field."

Phil nodded. "Ummm, I'm a bit worried about this list of products", referring to the job description. "I've never dealt with Microsoft Proxy Server 1.0, and I haven't dealt with Windows 95 OSR2 for a long while."

"Don't worry," Jason assured, "The Director is more an idea man. He just made a list of everything he's ever heard of. You'll just be supporting Windows Server 2003 and their flagship application."

Phil winced. He was a vanilla network administrator – supporting a custom app wasn't quite what he was looking for, but he desperately wanted to get out of his current job.

A few days later, Phil arrived for his interview. The company had rented smart offices on a new business park on the edge of town. He was ushered into the conference room, where he was joined by The Director and The Manager.

"So", said The Manager. "You've seen our brochure?"

"Yeah", said Phil, glancing at the glossy brochure in front of him with bright, Barbie-pink lettering all over it.

"You've seen a demo version of our application – what do you think?"

"Well, I think that it's great!", said Phil. He'd done his research – there were over 115 companies offering something very similar, and theirs wasn't anything special. "I particularly like the icons."

"Wonderful!" The Director cheered while firing up PowerPoint. "These are our servers. We rent some rack space in a data center 100 miles away." Phil looked at the projected picture. It showed a rack of a dozen servers.

"They certainly look nice." said Phil. They did look nice – brand new with green lights.

"Now, we also rent space in another data center on the other side of the country," The Manager added.

"This one is in a former cold-war bunker!" he said proudly. "It's very secure!" Phil looked up at another photo of some more servers.

"What we want the successful applicant to do is to take care of the servers on a day to day basis, but we also need to move those servers to the other data center", said The Director. "Without any interruption of service."

"Also, we need someone to set up the IT for the entire office. You know, email, file & print, internet access – that kind of thing. We've got a dozen salespeople starting next week, they'll all need email."

"And we need it to be secure."

"And we need it to be documented."

Phil was scribbled notes as best he could while the interviewing duo tag teamed him with questions.

"You'll also provide second line support to end users of the application."

"And day-to-day IT support to our own staff. Any questions?"

Phil looked up. "Ah… which back-end database does the application use?" he asked, expecting the answer would be SQL Server or perhaps Oracle, but The Director's reply surprised him.

"Oh, we wrote our own database from scratch. Martin wrote it." Phil realized his mouth was open, and shut it. The Director saw his expression, and explained. "You see, off the shelf databases have several disadvantages – the data gets fragmented, they're not quick enough, and so on. But don't have to worry about that – Martin takes care of the database. Do you have any more questions?"

Phil frowned. "So, to summarize: you want a data center guy to take care of your servers. You want someone to migrate the application from one data center to another, without any outage. You want a network administrator to set up, document and maintain an entire network from scratch. You want someone to provide internal support to the staff. And you want a second line support person to support the our flagship application."

"Exactly", beamed The Director paternally. "We want one person who can do all those things. Can you do that?"

Phil took a deep breath. "I don't know," he replied, and that was the honest answer.

"Right", The Manager said. "Well, if you have any questions, just give either of us a call, okay?"

Moments later, Phil was standing outside, clutching the garish brochure with the pink letters. His head was spinning. Could he do all that stuff? Did he want to? Was Martin a genius or a madman to reinvent the wheel with the celebrated database?

In the end, Phil was not offered the job and decided it might be best to stick it out at his old job for a while longer. After all, compared to Martin, maybe his job wasn't so bad after all.

[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: Mike's Job Search Job

Fri, 2025-05-23 08:30

Underqualified Mike S. is suffering a job hunt. "I could handle uD83D and uDC77 well enough, but I am a little short of uD83C and the all important uDFFE requirement."

 

Frank forecasts frustration. "The weather app I'm using seems to be a bit confused on my location as I'm on vacation right now." It would be a simple matter for the app to simply identify each location, if it can't meaningfully choose only one.

 

Marc Würth is making me hungry. Says Marc "I was looking through my Evernote notes for "transactional" (email service). It didn't find anything. Evernote, though, tried to be helpful and thought I was looking for some basil (German "Basilikum")."

 

"To be from or not from be," muses Michael R. Indeed, that is the question at Stansted Shakespeare airport.

 

That is not the King," Brendan commented. "I posted this on Discord, and my friend responded with "They have succeeded in alignment. Their AI is truly gender blind." Not only gender-blind but apparently also existence-blind as well. I think the Bard might have something quotable here as well but it escapes me. Comment section is open.

 

...and angels sing thee to thy rest. [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: A Trying Block

Thu, 2025-05-22 08:30

Mark sends us a very simple Java function which has the job of parsing an integer from a string. Now, you might say, "But Java has a built in for that, Integer.parseInt," and have I got good news for you: they actually used it. It's just everything else they did wrong.

private int makeInteger(String s) { int i=0; try { Integer.parseInt(s); } catch (NumberFormatException e) { i=0; return i; } i=Integer.parseInt(s); return i; }

This function is really the story of variable i, the most useless variable ever. It's doing its best, but there's just nothing for it to do here.

We start by setting i to zero. Then we attempt to parse the integer, and do nothing with the result. If it fails, we set i to zero again, just for fun, and then return i. Why not just return 0? Because then what would poor i get to do?

Assuming we didn't throw an exception, we parse the input again, storing its result in i, and then return i. Again, we treat i like a child who wants to help paint the living room: we give it a dry brush and a section of wall we're not planning to paint and let it go to town. Nothing it does matters, but it feels like a participant.

Now, Mark went ahead and refactored this function basically right away, into a more terse and clear version:

private int makeInteger(String s) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return 0; } }

He went about his development work, and then a few days later came across makeInteger reverted back to its original version. For a moment, he wanted to be mad at someone for reverting his change, but no- this was in an entirely different class. With that information, Mark went and did a search for makeInteger in the code, only to find 39 copies of this function, with minor variations.

There are an unknown number of copies of the function where the name is slightly different than makeInteger, but a search for Integer.parseInt implies that there may be many more.

[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: Buff Reading

Wed, 2025-05-21 08:30

Frank inherited some code that reads URLs from a file, and puts them into a collection. This is a delightfully simple task. What could go wrong?

static String[] readFile(String filename) { String record = null; Vector vURLs = new Vector(); int recCnt = 0; try { FileReader fr = new FileReader(filename); BufferedReader br = new BufferedReader(fr); record = new String(); while ((record = br.readLine()) != null) { vURLs.add(new String(record)); //System.out.println(recCnt + ": " + vURLs.get(recCnt)); recCnt++; } } catch (IOException e) { // catch possible io errors from readLine() System.out.println("IOException error reading " + filename + " in readURLs()!\n"); e.printStackTrace(); } System.out.println("Reading URLs ...\n"); int arrCnt = 0; String[] sURLs = new String[vURLs.size()]; Enumeration eURLs = vURLs.elements(); for (Enumeration e = vURLs.elements() ; e.hasMoreElements() ;) { sURLs[arrCnt] = (String)e.nextElement(); System.out.println(arrCnt + ": " + sURLs[arrCnt]); arrCnt++; } if (recCnt != arrCnt++) { System.out.println("WARNING: The number of URLs in the input file does not match the number of URLs in the array!\n\n"); } return sURLs; } // end of readFile()

So, we start by using a FileReader and a BufferedReader, which is the basic pattern any Java tutorial on file handling will tell you to do.

What I see here is that the developer responsible didn't fully understand how strings work in Java. They initialize record to a new String() only to immediately discard that reference in their while loop. They also copy the record by doing a new String which is utterly unnecessary.

As they load the Vector of strings, they also increment a recCount variable, which is superfluous since the collection can tell you how many elements are in it.

Once the Vector is populated, they need to copy all this data into a String[]. Instead of using the toArray function, which is built in and does that, they iterate across the Vector and put each element into the array.

As they build the array, they increment an arrCnt variable. Then, they do a check: if (recCnt != arrCnt++). Look at that line. Look at the post-increment on arrCnt, despite never using arrCnt again. Why is that there? Just for fun, apparently. Why is this check even there?

The only way it's possible for the counts to not match is if somehow an exception was thrown after vURLs.add(new String(record)); but before recCount++, which doesn't seem likely. Certainly, if it happens, there's something worse going on.

Now, I'm going to be generous and assume that this code predates Java 8- it just looks old. But it's worth noting that in Java 8, the BufferedReader class got a lines() function which returns a Stream<String> that can be converted directly toArray, making all of this code superfluous, but also, so much of this code is just superfluous anyway.

Anyway, for a fun game, start making the last use of every variable be a post-increment before it goes out of scope. See how many code reviews you can sneak it through!

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

Representative Line: What the FFFFFFFF

Tue, 2025-05-20 08:30

Combining Java with lower-level bit manipulations is asking for trouble- not because the language is inadequate to the task, but because so many of the developers who work in Java are so used to working at a high level they might not quite "get" what they need to do.

Victor inherited one such project, which used bitmasks and bitwise operations a great deal, based on the network protocol it implemented. Here's how the developers responsible created their bitmasks:

private static long FFFFFFFF = Long.parseLong("FFFFFFFF", 16);

So, the first thing that's important to note, is that Java does support hex literals, so 0xFFFFFFFF is a perfectly valid literal. So we don't need to create a string and parse it. But we also don't need to make a constant simply named FFFFFFFF, which is just the old twenty = 20 constant pattern: technically you've made a constant but you haven't actually made the magic number go away.

Of course, this also isn't actually a constant, so it's entirely possible that FFFFFFFF could hold a value which isn't 0xFFFFFFFF.

[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

Representative Line: Identifying the Representative

Mon, 2025-05-19 08:30

Kate inherited a system where Java code generates JavaScript (by good old fashioned string concatenation) and embeds it into an output template. The Java code was written by someone who didn't fully understand Java, but JavaScript was also a language they didn't understand, and the resulting unholy mess was buggy and difficult to maintain.

Why trying to debug the JavaScript, Kate had to dig through the generated code, which led to this little representative line:

dojo.byId('html;------sites------fileadmin------en------fileadmin------index.html;;12').setAttribute('isLocked','true');

The byId function is an alias to the browser's document.getElementById function. The ID on display here is clearly generated by the Java code, resulting in an absolutely cursed ID for an element in the page. The semicolons are field separators, which means you can parse the ID to get other information about it. I have no idea what the 12 means, but it clearly means something. Then there's that long kebab-looking string. It seems like maybe some sort of hierarchy information? But maybe not, because fileadmin appears twice? Why are there so many dashes? If I got an answer to that question, would I survive it? Would I be able to navigate the world if I understood the dark secret of those dashes? Or would I have to give myself over to our Dark Lords and dedicate my life to bringing about the end of all things?

Like all good representative lines, this one hints at darker, deeper evils in the codebase- the code that generates (or parses) this ID must be especially cursed.

The only element which needs to have its isLocked attribute set to true is the developer responsible for this: they must be locked away before they harm the rest of us.

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

Error'd: Teamwork

Fri, 2025-05-16 08:30

Whatever would we do without teamwork.

David doesn't know. "Microsoft Teams seems to have lost count (it wasn't a very big copy/paste)"

 

A follow-up from an anonymous doesn't know either. "Teams doing its best impression of a ransom note just to say you signed out. At least it still remembers how to suggest closing your browser. Small victories."

 

Bob F. just wants to make memes. "I've been setting my picture widths in this document to 7.5" for weeks, and suddenly after the latest MS Word update, Microsoft thinks 7.5 is not between -22.0 and 22.0. They must be using AI math to determine this."

 

Ewan W. wonders "a social life: priceless...?". Ewan has some brand confusion but after the Boom Battle Bar I bet I know why.

 

Big spender Bob B. maybe misunderstands NaN. He gleefully exclaims "I'm very happy to get 15% off - Here's hoping the total ends up as NaN and I get it all free." Yikes. 191.78-NaN is indeed NaN, but that just means you're going to end up owing them NaN. Don't put that on a credit card!

 

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

CodeSOD: A Jammed Up Session

Thu, 2025-05-15 08:30

Andre has inherited a rather antique ASP .Net WebForms application. It's a large one, with many pages in it, but they all follow a certain pattern. Let's see if you can spot it.

protected void btnSearch_Click(object sender, EventArgs e) { ArrayList paramsRel = new ArrayList(); paramsRel["Name"] = txtNome.Text; paramsRel["Date"] = txtDate.Text; Session["paramsRel"] = paramsRel; List<Client> clients = Controller.FindClients(); //Some other code }

Now, at first glance, this doesn't look terrible. Using an ArrayList as a dictionary and frankly, storing a dictionary in the Session object is weird, but it's not an automatic red flag. But wait, why is it called paramsRel? They couldn't be… no, they wouldn't…

public List<Client> FindClients() { ArrayList paramsRel = (ArrayList)Session["paramsRel"]; string name = (string)paramsRel["Name"]; string dateStr = (string)paramsRel["Date"]; DateTime date = DateTime.Parse(dateStr); //More code... }

Now there's the red flag. paramsRel is how they pass parameters to functions. They stuff it into the Session, then call a function which retrieves it from that Session.

This pattern is used everywhere in the application. You can see that there's a vague gesture in the direction of trying to implement some kind of Model-View-Controller pattern (as FindClients is a member of the Controller object), but that modularization gets undercut by everything depending on Session as a pseudoglobal for passing state information around.

The only good news is that the Session object is synchronized so there's no thread safety issue here, though not for want of trying.

.comment { border: none; } [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: itouhhh…

Wed, 2025-05-14 08:30

Frequently in programming, we can make a tradeoff: use less (or more) CPU in exchange for using more (or less) memory. Lookup tables are a great example: use a big pile of memory to turn complicated calculations into O(1) operations.

So, for example, implementing itoa, the C library function for turning an integer into a character array (aka, a string), you could maybe make it more efficient using a lookup table.

I say "maybe", because Helen inherited some C code that, well, even if it were more efficient, it doesn't help because it's wrong.

Let's start with the lookup table:

char an[1000][3] = { {'0','0','0'},{'0','0','1'},{'0','0','2'},{'0','0','3'},{'0','0','4'},{'0','0','5'},{'0','0','6'},{'0','0','7'},{'0','0','8'},{'0','0','9'}, {'0','1','0'},{'0','1','1'},{'0','1','2'},{'0','1','3'},{'0','1','4'},{'0','1','5'},{'0','1','6'},{'0','1','7'},{'0','1','8'},{'0','1','9'}, …

I'm abbreviating the lookup table for now. This lookup table is meant to be use to convert every number from 0…999 into a string representation.

Let's take a look at how it's used.

int ll = f->cfg.len_len; long dl = f->data_len; // Prepare length if ( NULL == dst ) { dst_len = f->data_len + ll + 1 ; dst = (char*) malloc ( dst_len ); } else //if( dst_len < ll + dl ) if( dst_len < (unsigned) (ll + dl) ) { // TO DOO - error should be processed break; } long i2; switch ( f->cfg.len_fmt) { case ASCII_FORM: { if ( ll < 2 ) { dst[0]=an[dl][2]; } else if ( ll < 3 ) { dst[0]=an[dl][1]; dst[1]=an[dl][2]; } else if ( ll < 4 ) { dst[0]=an[dl][0]; dst[1]=an[dl][1]; dst[2]=an[dl][2]; } else if ( ll < 5 ) { i2 = dl / 1000; dst[0]=an[i2][2]; i2 = dl % 1000; dst[3]=an[i2][2]; dst[2]=an[i2][1]; dst[1]=an[i2][0]; } else if ( ll < 6 ) { i2 = dl / 1000; dst[0]=an[i2][1]; dst[1]=an[i2][2]; i2 = dl % 1000; dst[4]=an[i2][2]; dst[3]=an[i2][1]; dst[2]=an[i2][0]; } else { // General case for ( int k = ll ; k > 0 ; k-- ) { dst[k-1] ='0' + dl % 10; dl/=10; } } dst[dl]=0; break; } }

Okay, we start with some reasonable bounds checking. I have no idea what to make of a struct member called len_len- the length of the length? I'm lacking some context here.

Then we get into the switch statement. For all values less than 4 digits, everything makes sense, more or less. I'm not sure what the point of using a 2D array for you lookup table is if you're also copying one character at a time, but for such a small number of copies I'm sure it's fine.

But then we get into the len_lens longer than 3, and we start dividing my 1000 so that our lookup table continues to work. Which, again, I guess is fine, but I'm still left wondering why we're doing this, why this specific chain of optimizations is what we need to do. And frankly, why we couldn't just use itoa or a similar library function which already does this and is probably more optimized than anything I'm going to write.

When we have an output longer than 5 characters, we just use a naive for-loop and some modulus as our "general" case.

So no, I don't like this code. It reeks of premature optimization, and it also has the vibe of someone starting to optimize without fully understanding the problem they were optimizing, and trying to change course midstream without changing their solution.

But there's a punchline to all of this. Because, you see, I skipped most of the lookup table. Would you like to see how it ends? Of course you do:

{'9','8','0'},{'9','8','1'},{'9','8','2'},{'9','8','3'},{'9','8','4'},{'9','8','5'},{'9','8','6'},{'9','8','7'},{'9','8','8'},{'9','8','9'} };

The lookup table doesn't work for values from 990 to 999. There are just no entries there. All this effort to optimize converting integers to text and we end up here: with a function that doesn't work for 1% of the possible values it could receive. And, given that the result is an out-of-bounds array access, it fails with everyone's favorite problem: undefined behavior. Usually it'll segfault, but who knows! Maybe it returns whatever bytes it finds? Maybe it sends the nasal demons after you. The compiler is allowed to do anything.

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

CodeSOD: Exactly a Date

Tue, 2025-05-13 08:30

Alexandar sends us some C# date handling code. The best thing one can say is that they didn't reinvent any wheels, but that might be worse, because they used the existing wheels to drive right off a cliff.

try { var date = DateTime.ParseExact(member.PubDate.ToString(), "M/d/yyyy h:mm:ss tt", null); objCustomResult.PublishedDate = date; } catch (Exception datEx) { }

member.PubDate is a Nullable<DateTime>. So its ToString will return one of two things. If there is a value there, it'll return the DateTimes value. If it's null, it'll just return an empty string. Attempting to parse the empty string will throw an exception, which we helpfully swallow, do nothing about, and leave objCustomResult.PublishedDate in whatever state it was in- I'm going to guess null, but I have no idea.

Part of this WTF is that they break the advantages of using nullable types- the entire point is to be able to handle null values without having to worry about exceptions getting tossed around. But that's just a small part.

The real WTF is taking a DateTime value, turning it into a string, only to parse it back out. But because this is in .NET, it's more subtle than just the generation of useless strings, because member.PubDate.ToString()'s return value may change depending on your culture info settings.

Which sure, this is almost certainly server-side code running on a single server with a well known locale configured. So this probably won't ever blow up on them, but it's 100% the kind of thing everyone thinks is fine until the day it's not.

The punchline is that ToString allows you to specify the format you want the date formatted in, which means they could have written this:

var date = DateTime.ParseExact(member.PubDate.ToString("M/d/yyyy h:mm:ss tt"), "M/d/yyyy h:mm:ss tt", null);

But if they did that, I suppose that would have possibly tickled their little grey cells and made them realize how stupid this entire block of code was?

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

CodeSOD: Would a Function by Any Other Name Still be WTF?

Mon, 2025-05-12 08:30

"Don't use exception handling for normal flow control," is generally good advice. But Andy's lead had a PhD in computer science, and with that kind of education, wasn't about to let good advice or best practices tell them what to do. That's why, when they needed to validate inputs, they wrote code C# like this:

public static bool IsDecimal(string theValue) { try { Convert.ToDouble(theValue); return true; } catch { return false; } }

They attempt to convert, and if they succeed, great, return true. If they fail, an exception gets caught, and they return false. What could be simpler?

Well, using the built in TryParse function would be simpler. Despite its name, actually avoids throwing an exception, even internally, because exceptions are expensive in .NET. And it is already implemented, so you don't have to do this.

Also, Decimal is a type in C#- a 16-byte floating point value. Now, I know they didn't actually mean Decimal, just "a value with 0 or more digits behind the decimal point", but pedantry is the root of clarity, and the naming convention makes this bad code unclear about its intent and purpose. Per the docs there are Single and Double values which can't be represented as Decimal and trigger an OverflowException. And conversely, Decimal loses precision if converted to Double. This means a value that would be represented as Decimal might not pass this function, and a value that can't be represented as Decimal might, and none of this actually matters but the name of the function is bad.

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

Error'd: Cuts Like a Knife

Fri, 2025-05-09 08:30

Mike V. shares a personal experience with the broadest version of Poe's Law: "Slashdot articles generally have a humorous quote at the bottom of their articles, but I can't tell if this displayed usage information for the fortune command, which provides humorous quotes, is a joke or a bug." To which I respond with the sharpest version of Hanlon's Razor: never ascribe to intent that which can adequately be explained by incompetence.

 

Secure in his stronghold, Stewart snarks "Apparently my router is vulnerable because it is connected to the internet. So glad I pay for the premium security service."

 

The Beast in Black is back with more dross, asking "Oh GitLab, you so silly - y u no give proper reason?"

 

An anonymous reader writes "I got this when I tried to calculate the shipping costs for buying the Playdate game device. Sorry, I don't have anything snarky to say, please make something up." The comments section is open for your contributions.

 

Ben S. looking for logic in all the wrong places, wonders "This chart from my electric utility's charitable giving program kept my alumni group guessing all day. The arithmetic checks out, but what does the gray represent, and why is the third chart at a different scale?"

 

[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: Leap to the Past

Thu, 2025-05-08 08:30

Early in my career, I had the misfortune of doing a lot of Crystal Reports work. Crystal Reports is another one of those tools that lets non-developer, non-database savvy folks craft reports. Which, like so often happens, means that the users dig themselves incredible holes and need professional help to get back out, because at the end of the day, when the root problem is actually complicated, all the helpful GUI tools in the world can't solve it for you.

Michael was in a similar position as I was, but for Michael, there was a five alarm fire. It was the end of the month, and a bunch of monthly sales reports needed to be calculated. One of the big things management expected to see was a year-over-year delta on sales, and they got real cranky if the line didn't go up. If they couldn't even see the line, they went into a full on panic and assumed the sales team was floundering and the company was on the verge of collapse.

Unfortunately, the report was spitting out an error: "A day number must be between 1 and the number of days in the month."

Michael dug in, and found this "delight" inside of a function called one_year_ago:

Local StringVar yearStr := Left({?ReportToDate}, 4); Local StringVar monthStr := Mid({?ReportToDate}, 5, 2); Local StringVar dayStr := Mid({?ReportToDate}, 7, 2); Local StringVar hourStr := Mid({?ReportToDate}, 9, 2); Local StringVar minStr := Mid({?ReportToDate}, 11, 2); Local StringVar secStr := Mid({?ReportToDate}, 13, 2); Local NumberVar LastYear; LastYear := ToNumber(YearStr) - 1; YearStr := Replace (toText(LastYear),'.00' , '' ); YearStr := Replace (YearStr,',' , '' ); //DateTime(year, month, day, hour, min, sec); //Year + Month + Day + Hour + min + sec; // string value DateTime(ToNumber(YearStr), ToNumber(MonthStr), ToNumber(dayStr), ToNumber(HourStr), ToNumber(MinStr),ToNumber(SecStr) );

We've all seen string munging in date handling before. That's not surprising. But what's notable about this one is the day on which it started failing. As stated, it was at the end of the month. But which month? February. Specifically, February 2024, a leap year. Since they do nothing to adjust the dayStr when constructing the date, they were attempting to construct a date for 29-FEB-2023, which is not a valid date.

Michael writes:

Yes, it's Crystal Reports, but surprisingly not having date manipulation functions isn't amongst it's many, many flaws. It's something I did in a past life isn't it??

The fix was easy enough- rewrite the function to actually use date handling. This made a simpler, basically one-line function, using Crystal's built in functions. That fixed this particular date handling bug, but there were plenty more places where this kind of hand-grown string munging happened, and plenty more opportunities for the report to fail.

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

Editor's Soapbox: AI: The Bad, the Worse, and the Ugly

Wed, 2025-05-07 08:30
…the average American, I think, has fewer than three friends. And the average person has demand for meaningfully more, I think it's like 15 friends or something, right?
- Mark Zuckerberg, presumably to one of his three friends

Since even the President of the United States is using ChatGPT to cheat on his homework and make bonkers social media posts these days, we need to have a talk about AI.

Right now, AI is being shoe-horned into everything, whether or not it makes sense. To me, it feels like the dotcom boom again. Millipedes.com! Fungus.net! Business plan? What business plan? Just secure the domain names and crank out some Super Bowl ads. We'll be RICH!

In fact, it's not just my feeling. The Large Language Model (LLM) OpenAI is being wildly overvalued and overhyped. It's hard to see how it will generate more revenue while its offerings remain underwhelming and unreliable in so many ways. Hallucination, bias, and other fatal flaws make it a non-starter for businesses like journalism that must have accurate output. Why would anyone convert to a paid plan? Even if there weren't an income problem—even if every customer became a paying customer—generative AI's exorbitant operational and environmental costs are poised to drown whatever revenue and funding they manage to scrape together.

Lest we think the problem is contained to OpenAPI or LLMs, there's not a single profitable AI venture out there. And it's largely not helping other companies to be more profitable, either.

A moment like this requires us to step back, take a deep breath. With sober curiosity, we gotta explore and understand AI's true strengths and weaknesses. More importantly, we have to figure out what we are and aren't willing to accept from AI, personally and as a society. We need thoughtful ethics and policies that protect people and the environment. We need strong laws to prevent the worst abuses. Plenty of us have already been victimized by the absence of such. For instance, one of my own short stories was used by Meta without permission to train their AI.

The Worst of AI
Sadly, it is all too easy to find appalling examples of all the ways generative AI is harming us. (For most of these, I'm not going to provide links because they don't deserve the clicks):

  • We all know that person who no longer seems to have a brain of their own because they keep asking OpenAI to do all of their thinking for them.
  • Deepfakes deliberately created to deceive people.
  • Cheating by students.
  • Cheating by giant corporations who are all too happy to ignore IP and copyright when it benefits them (Meta, ahem).
  • Piles and piles of creepy generated content on platforms like Youtube and TikTok that can be wildly inaccurate.
  • Scammy platforms like DataAnnotation, Mindrift, and Outlier that offer $20/hr or more for you to "train their AI." Instead, they simply gather your data and inputs and ghost the vast majority of applicants. I tried taking DataAnnotation's test for myself to see what would happen; after all, it would've been nice to have some supplemental income while job hunting. After several weeks, I still haven't heard back from them.
  • Applicant Tracking Systems (ATS) block job applications from ever reaching a human being for review. As my job search drags on, I feel like my life has been reduced to a tedious slog of keyword matching. Did I use the word "collaboration" somewhere in my resume? Pass. Did I use the word "teamwork" instead? Fail. Did I use the word "collaboration," but the AI failed to detect it, as regularly happens? Fail, fail, fail some more. Frustrated, I and no doubt countless others have been forced to turn to other AIs in hopes of defeating those AIs. While algorithms battle algorithms, companies and unemployed workers are all suffering.
  • Horrific, undeniable environmental destruction.
  • Brace yourself: a 14 year-old killed himself with the encouragement of the chatbot he'd fallen in love with. I can only imagine how many more young people have been harmed and are being actively harmed right now.

The Best of AI?
As AI began to show up everywhere, as seemingly everyone from Google to Apple demanded that I start using it, I had initially responded with aversion and resentment. I never bothered with it, I disabled it wherever I could. When people told me to use it, I waved them off. My life seemed no worse for it.

Alas, now AI completely saturates my days while job searching, bringing on even greater resentment. Thousands of open positions for AI-based startups! Thousands of companies demanding expertise in generative AI as if it's been around for decades. Well, gee, maybe my hatred and aversion is hurting my ability to get hired. Am I being a middle-aged Luddite here? Should I be learning more about AI (and putting it on my resume)? Wouldn't I be the bigger person to work past my aversion in order to learn about and highlight some of the ways we can use AI responsibly?

I tried. I really tried. To be honest, I simply haven't found a single positive generative AI use-case that justifies all the harm taking place.

So, What Do We Do?
Here are some thoughts: don't invest in generative AI or seek a job within the field, it's all gonna blow. Lobby your government to investigate abuses, protect people, and preserve the environment. Avoid AI usage and, if you're a writer like me, make clear that AI is not used in any part of your process. Gently encourage that one person you know to start thinking for themselves again.

Most critically of all: wherever AI must be used for the time being, ensure that one or more humans review the results.

li { margin: 0.5em; list-style: circle } [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 Big Pictures

Tue, 2025-05-06 08:30

Loading times for web pages is one of the key metrics we like to tune. Users will put up with a lot if they feel like they application is responsive. So when Caivs was handed 20MB of PHP and told, "one of the key pages takes like 30-45 seconds to load. Figure out why," it was at least a clear goal.

Combing through that gigantic pile of code to try and understand what was happening was an uphill battle. Eventually, Caivs just decided to check the traffic logs while running the application. That highlighted a huge spike in traffic every time the page loaded, and that helped Caivs narrow down exactly where the problem was.

$first_image = ''; foreach($images as $the_image) { $image = $the_image['url']; if(file_exists($config->base_url.'/uploads/'.$image)) { if($first_image=='') { $first_image = $image; } $image_dimensions = '&w=648&h=432'; $get_dimensions = getimagesize('http://old.datacenter.ip.address/'.$config->base_url.'/uploads/'.$image); if($get_dimensions[0] < $get_dimensions[1]) $image_dimensions = '&h=432'; echo '<li>'.$config->base_url.'/timthumb.php?src='.$config->base_url.'/uploads/'.$image.'&w=125&h=80&zc=1'), 'javascript:;', array('onclick'=>'$(\'.image_gallery .feature .image\').html(\''.$config->base_url.'/timthumb.php?src='.$config->base_url.'/uploads/'.$image.$image_dimensions.'&zc=1').'\');$(\'.image_gallery .feature .title\').show();$(\'.image_gallery .feature .title\').html("'.str_replace('"', '', $the_image['Image Description']).'");$(\'.image_gallery .bar ul li a\').removeClass(\'active\');$(\'.image_gallery .bar ul li\').removeClass(\'active\');$(this).addClass(\'active\');$(this).parents(\'li\').addClass(\'active\');sidebarHeight();curImg=$(this).attr(\'id\');translate()','id'=>$img_num)).'</li>'; $img_num++; } }

For every image they want to display in a gallery, they echo out a list item for it, which that part makes sense- more or less. The mix of PHP, JavaScript, JQuery, and HTML tags is ugly and awful and I hate it. But that's just a prosaic kind of awful, background radiation of looking at PHP code. Yes, it should be launched into the Kupier belt (it doesn't deserve the higher delta-V required to launch it into the sun), but that's not why we're here.

The cause of the long load times was in the lines above- where for each image, we getimagesize- a function which downloads the image and checks its stats, all so we can set $image_dimensions. Which, presumably, the server hosting the images uses the query string to resize the returned image.

All this is to check- if the height is greater than the width we force the height to be 432 pixels, otherwise we force the whole image to be 648x432 pixels.

Now, the server supplying those images had absolutely no caching, so that meant for every image request it needed to resize the image before sending. And for reasons which were unclear, if the requested aspect ratio were wildly different than the actual aspect ratio, it would also sometimes just refused to resize and return a gigantic original image file. But someone also had thought about the perils of badly behaved clients downloading too many images, so if a single host were requesting too many images, it would start throttling the responses.

When you add all this up, it meant that this PHP web application was getting throttled by its own file server, because it was requesting too many images, too quickly. Any reasonable user load hitting it would be viewed as an attempted denial of service attack on the file hosting backend.

Caivs was able to simply remove the check on filesize, and add a few CSS rules which ensured that files in the gallery wouldn't misbehave terribly. The performance problems went away- at least for that page of the application. Buried in that 20MB of PHP/HTML code, there were plenty more places where things could go wrong.

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

Pages