The Daily WTF

Subscribe to The Daily WTF feed
Curious Perversions in Information Technology
Updated: 2 hours 24 min ago

Error'd: Operation Erred Successfully

Fri, 2024-09-27 08:30

"Clouds obscure the result," reports Mike T.'s eight-ball. "It's a shame when the cloud and the browser disagree," he observed.

 

"Ivent is being really damn buggy" muttered Vitr S. "Looks like the testing team is going to have to become Undefined Undefined in their employment records."

 

"What's a numeric character?" wonders Ross K. "Actually any character in the "Amount required in words" field provokes this message. And it won't accept an empty field either." I believe that I,V,X,L,C, and D are numeric characters. You should try those.

 

"Looks like they've encountered a McError," chortled Shaun M. "As a dev, this notification has me thinking about McDonalds more than their marketing notifications do!" Very clever of them, wouldn't you say Shaun?

 

Finally, faithful Michael R. is job-hunting (hint) and found a position he is specially ell-suited or. "I'm very good at defineing, eveloping and riving innovation. " est of luck!

 

[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: True Parseimony

Thu, 2024-09-26 08:30

We've seen this pattern many times here:

return (someCondition) ? true : false;

or

if (someCondition) { return true; } else { return false; }

There are many variations on it, all of which highlight someone's misunderstanding of boolean expressions. Today Kerry sends us a "fun" little twist, in C#.

return (someCondition || someOtherCondition) ? Boolean.Parse("true") : Boolean.Parse("false");

The conditions have been elided by Kerry, but they're long and complicated, rendering the statement less readable than it appears here.

But here we've taken the "if-condition-return-condition" pattern and added needless string parsing to 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

Space for Queries

Wed, 2024-09-25 08:30

Maria was hired as a consultant by a large financial institution. The institution had a large pile of ETL scripts, reports, analytics dashboards, and the like, which needed to be supported. The challenge is that everyone who wasn't a developer had built the system. Due to the vagaries of internal billing, hiring IT staff to do the work would have put it under a charge code which would have drained the wrong budget, so they just did their best.

The quality of the system wasn't particularly good, and it required a lot of manual support to actually ensure that it kept working. It was several hundred tables, with no referential integrity constraints on them, no validation rules, no concept of normalization (or de-normalization- it was strictly abnormalied tables) and mostly stringly typed data. It all sat in an MS SQL Server, and required daily manual runs of stored procedures to actually function.

Maria spent a lot of time exploring the data, trying to understand the various scripts, stored procedures, manual processes, and just the layout of the data. As part of this, she ran SELECT queries directly from the SQL Server Management Studio (SMSS), based on the various ETL and reporting jobs.

One reporting step queried the "BusinessValue" column from a table. So Maria wrote a query that was similar, trying to understand the data in that column:

SELECT Id, CostCentreCode, BusinessValue FROM DataBusinessTable

This reported "Invalid Column Name: 'BusinessValue'".

Maria re-read the query she was copying. She opened the definition of the table in the SMSS UI. There was a column clearly labeled "BusinessValue". She read it carefully, ensuring that there wasn't a typo or spelling error, either in her query or the table definition.

After far too much time debugging, she had the SMSS tool generate the CREATE TABLE statement to construct the table.

CREATE TABLE DataBusinessTable ([Id] Number IDENTITY, …, [BusinessValue ] TEXT )

Maria felt like she'd fallen for the worst troll in the history of trolling. The column name had a space at the end.

According to Maria, this has since been "fixed" in SQL Server- you can now run queries which omit trailing whitespace from names, but at the time she was working on this project, that's clearly not how things worked.

The fact that this trailing whitespace problem was common enough that the database engine added a feature to avoid it is in fact, the real WTF.

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

CodeSOD: Secure Cryptography

Tue, 2024-09-24 08:30

Governments have a difficult relationship with cryptography. Certainly, they benefit from having secure, reliable and fast encryption. Arguably, their citizens also benefit- I would argue that being able to, say, handle online banking transactions securely is a net positive. But it creates a prisoner's dilemma: malicious individuals may conceal their activity behind encryption. From the perspective of a state authority, this is bad.

Thus, you get the regular calls for a cryptosystem which allows secure communications but also allows the state to snoop on those messages. Of course, if you intentionally weaken a cryptographic system so that some people can bypass it, you've created a system which anyone can bypass. You can't have secure encryption which also allows snooping, any more than you can have an even prime number larger than two.

This leaves us in a situation where mathematicians and cryptography experts are shouting, "This isn't possible!" and cops and politicians are shouting "JUST NERD HARDER!" back.

Well, today's anonymous submitter found a crypto library which promises to allow secure communications and allow nation states to break that encryption. They've nerded harder! Let's take a look at some of their C code.

unsigned long int alea(void) { FILE * f1 = 0; unsigned char val = 0; /*float rd = 0;*/ unsigned long int rd = 0; f1 = fopen("/dev/random","r"); fread(&val,sizeof(unsigned char),1,f1); /*rd = (float)(val) / (ULONG_MAX);*/ rd = (unsigned long int)((val) / (UCHAR_MAX)); fclose(f1); return rd; }

This function reads a byte from /dev/random to get us some random data for key generation. Unfortunately, it's using the XKCD algorithm: this function always returns 0. Note that they divide val by UCHAR_MAX before casting val to an unsigned long int. Which, there's also the issue here that 8 bits of entropy are always going to be 8 bits of entropy- casting it to an unsigned long int isn't going to be any more random than just passing back the 8-bits, because you only read 8 bits of randomness. There's also no reason why they couldn't have simply read an unsigned long's worth of random data.

I appreciate the comment which indicates that rd used to be a floating point number. It doesn't make this code any better, but it's nice to see that they keep trying.

Let's also take a peek at a use-after-free waiting to happen:

void MY_FREE(void *p) { if (p == NULL) return; free(p); p = NULL; }

They wrote their own MY_FREE function, which adds a NULL check around the pointer- don't free the memory if the pointer points at NULL. Nothing wrong with that (though this is better enforced structurally with clear ownership of memory, and not through conditional checks, so actually, yes, there's something wrong with that). After we free the memory pointed to by the pointer, we set the pointer equal to NULL.

Except we don't. We set the local variable to NULL. So when the code does things like:

if(v->aliveness == NULL) { MY_FREE(v); return(v); }

v is a pointer to our struct. If the aliveness value is NULL, we want to delete that data from memory, so we call MY_FREE, and then we return our pointer to the struct- which is unchanged and definitely not null. It's pointing to what is now freed memory. If anyone touches it, the whole program blows up.

Here's the upshot: it's almost a guarantee that this program has undefined behavior in there, someplace. This means the compiler is free to do anything, include implement a dual custody cryptographic algorithm which is magically secure and prevents abuse by state actors. It's as likely as nasal demons.

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

Mon, 2024-09-23 08:30

Craig had been an IT manager at an immigration services company for several years, but was ready to move on. And for good reason- Craig had suffered his share of moronic helldesk nonsense and was ready to let someone else deal with it. This meant participating in interviews for his replacement.

Craig had given a rather generous three months notice, and very quickly the remaining three months were taken up with interviewing possible replacements. Each interview followed the same basic pattern: Craig would greet the candidate, escort them down to the fish-bowl style conference room in the center of the office, where a panel of interviewers ran through a mix of behavioral and technical questions. The interviews were consistently disappointing, and so they'd move on to the next candidate, hoping for an improvement.

After the first few interviews, he started making up questions about potentially horrible IT related disasters. "You see an executive using scissors to cut the Ethernet cable. When pressed, they explain that they want their connection to be wireless. What do you do?" "It's the holiday season, and you see someone trying to extend Christmas lights in the break room using a suicide cable, what do you do?" "You discover one of the technicians has been hiding a bottle of whiskey in the server room. What do you do?"

This kept Craig entertained, but didn't get them any closer to hiring any of these candidates.

One day, they brought in another candidate, and Craig ran the standard interview. His mind wasn't really on the interview- the candidate's resume wasn't the best they'd seen, and it took only a few minutes to establish that they probably weren't the best fit for the role. So Craig spent the time thinking more about whatever absurd question he was going to ask than what was going on in front of him.

His mind drifted off, and his eyes wandered around the office. They strayed to the corner office, also fishbowl style, where the CEO sat. And that's when Craig realized he wasn't going to need to make anything up for today's interview.

"What would you do if you saw a member of staff washing their keyboard with Evian mineral water, while sitting at their desk, their computer still on and keyboard still plugged in?"

The candidate was bemused, and just sat silently. For a long beat, they just watched Craig. Craig, obligingly, pointed back to the CEO's office, where the CEO was in the process of doing exactly what Craig had described.

The candidate took in the scene. Saw the placard announcing that as the CEO's office. Saw not just one, but two open bottles of Evian. Saw the water spreading everywhere, as the CEO hadn't considered things like "have some paper towels on hand".

The candidate turned back to Craig, and eloquently shrugged. There was a world-weariness in the shrug, that spoke to long experience with situations like this. It was the shrug of an IT manager that was going to keep a healthy stock of replacement keyboards, and never ever let the CEO have a laptop.

In the end, it was that candidate who got the job, not because they had the best interview, or the best resume, but because they knew what they were getting in to, and were ready to deal with it.

The keyboard, however, wasn't so lucky. "How else was I supposed to get the breadcrumbs out of it?" the CEO asked while Craig replaced the keyboard.

[Advertisement] Picking up NuGet is easy. Getting good at it takes time. ProGet costs less than half of Artifactory and is just as good. Our easy-to-read comparison page lays out the editions, features, and pricing of the different editions of ProGet and Artifactory.Learn More.
Categories: Computer

Error'd: A Dark Turn

Fri, 2024-09-20 08:30

You may call it equity, or equinox or whatever woke term you like but you can't sugarcoat what those of us in the North know: the Southern Hemisphere is stealing the very essence of our dwindling days. Sucking out the insolation like a milkshake and slurping it all across the equator.
Rage, rage against the dying of the light!

Meanwhile. Steven B. reminded us of an Error'd I'm pretty sure we've seen before, but it's too dark in here to find now. "I think Microsoft need a bit more tuning on their Office365 anti-spam filters," he suggests.

 

Jan confirms that broken test-in-production data will never die. "I hope the size of the content of the package is in fact 200mm x 78mm x 61mm, as this is what I ordered."

 

Jozsef thinks the Wise fees don't add up, claiming "The email explains why they chose this amount, but it's still funny, and technically wrong." I agree it's funny but I don't see what's wrong. Is Jozsef arguing that lowering a cost by 0% is not actually lowering anything? I see his position but I consider lowering by 0% to be simply the degenerate case of lowering, just as highering a thing by 0% is a degenerate use of antonyms."

 

Karun R. thinks this form is funny, snickering "When is optional not optional ? Wont let me submit until I entered something in the field."

 

Finally Mike just keeps tilting at this old windmill: "Is encoding finally fixed in the new version? 😂"

 

[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: A Managed Session

Thu, 2024-09-19 08:30

Some time ago, Roald started an internship on a ASP .Net application. It didn't take long to find some "special" code.

public string RetrieveSessionString(string sessionName) { try { return Session[sessionName].ToString(); } catch (NullReferenceException) { return null; } }

The Session variable is a session object for this user session. Each request carries a token which allows us to pair a Session with a user, making a cross-request per-user global object. That is what it is- but it's weird that we call the parameter sessionName. Maybe that's just a bad parameter name- it might be better called sessionKey or something like that.

Of course, the real issue here is it's null handling. Calling ToString on a key that doesn't exist throws a NullReferenceException, so we handle it just to return a null, thus making future NullReferenceExceptions somebody else's problem. Arguably, an empty string would be a better behavior. Still, I hate it.

But Roald also found this function's evil twin:

public Dictionary<string, string> RetrieveSessionDictionary(string sessionName) { try { return (Dictionary<string, string>)Session[sessionName]; } catch (NullReferenceException) { return null; } }

This is the same function, but instead of fetching a string, it fetches a dictionary of string/string pairs. It does the same null handling, but notably, doesn't do any error handling for situations where the cast fails.

And suddenly, this makes more sense. They're using the word "session" in two different contexts. There's the Session- a series of HTTP requests sharing the same token- and there's a user's session- settings which represent a unit of work. They're storing a dictionary representing a session in the Session object.

Which leaves this code feeling just… gross. It makes sense, and aside from the awful null handling, I understand why it works this way. It's just awkward and uncomfortable and annoying. I dislike it.

Also, functions which are name RetrieveBlahAsType are generally an antipattern. Either there should be some generics, or type conversions should be left to the caller- RetrieveSession(sessionName).ToString() is clearer with its intent than RetrieveSessionString(sessionName). Maybe that's just my hot take- I just hate it when functions return something converted away from its canonical representation; I can do that myself, thank you.

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

CodeSOD: String in your Colon

Wed, 2024-09-18 08:30

Anders sends us an example which isn't precisely a WTF. It's just C handling C strings. Which, I guess, when I say it that way, is a WTF.

while(modPath != NULL) { p = strchr(modPath, ':'); if(p != NULL) { *p++ = '\0'; } dvModpathCreate(utSymCreate(modPath)); modPath = p; } while(modPath != NULL);

We start with a variable called modPath which points to a C string. So long as modPath is not null, we're going to search through the string.

The string is in a : separated format, e.g., foo:bar:goo. We want to split it. This function does this by being very C about it.

It uses strchr to find the address of the first colon. If we think about this in C strings, complete with null terminators, it looks something like this:

"foo:bar:goo\0" ^ ^ | | | p modPath

We then replace : with \0 and increment p, doing a "wonderful" blend of using the dereference operator and the post-increment operator and an assignment to accomplish a lot in one line.

"foo\0bar:goo\0" ^ ^ | | | p modPath

So now, modPath points at a terminated string foo, which we then pass down through some functions. Then we set it equal to p.

"foo\0bar:goo\0" ^ | p modPath

This repeats until strchr doesn't find a :, at which point it returns NULL. Our loop is guarded by a check that modPath (which gets set equal to p) can't be null, so that breaks us out of the loop.

And enters us immediately into another, single line loop with no body, which immediately exits as well. I suspect that originally this was written as a do{}while, and then someone realized that it could just be a plain while{}, and completely forgot to remove the second while clause.

This is, honestly, a pretty common idiom in C. It's arguably wrong to even put it here; aside from the bad while clause, you'll see this kind of string handling all the time. But, maybe, that is the WTF.

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

CodeSOD: String Du Jour

Tue, 2024-09-17 08:30

It's not brought up frequently, but a "CodeSOD" is a "Code Sample of the Day". Martin brings us this function, entitled StringOfToday. It's in VB.Net, which, as we all know, has date formatting functions built in.

Public Function StringOfToday() As String Dim d As New DateTime d = Now Dim DayString As String If d.Day < 10 Then DayString = "0" & d.Day.ToString Else DayString = d.Day.ToString End If Dim MonthString As String If d.Month < 10 Then MonthString = "0" & d.Month.ToString Else MonthString = d.Month.ToString End If Dim YearString As String = d.Year.ToString Return YearString & MonthString & DayString End Function

There's not much new here, when it comes to formatting dates as strings. Grab the date, and pad it if it's less than 10. Grab the month, and pad it if it's less than 10. Grab the year, which will be 4 digits anytime within the last 2,000 years or so, so we don't need to pad it. Concatenate it all together, and voila: a date string.

Mostly, I just enjoy this because of the name. StringOfToday. It's like I'm in a restaurant. "Excuse me, waiter, what's the string of the day?" "Ah, a piquant 8 digit numeric string, hand concatenated using the finest ampersands, using a bounds checked string type." "Oh, excellent, I'm allergic to null terminators. I'll have that."

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

CodeSOD: A Clever Base

Mon, 2024-09-16 08:30

Mark worked with the kind of programmer who understood the nuances and flexibility of PHP on a level like none other. This programmer also wanted to use all of those features.

This resulted in the Base class, from which all other classes descend.

Mark, for example, was trying to understand how the status field on a Widget got set. So, he pulled up the Widget code:

class Widget extends Base { public function getOtherWidgets(){return $this->widgetPart->otherWidgets;} public function getStatus(){ $this->otherWidgets; if(isset($this->status))return $this->status; } }

So, getStatus doesn't always return a value. That's fun, but I guess it doesn't return a value when $this->status doesn't have a value, we can let that slide.

The line above that return is odd, though. $this->otherWidgets. That sure as heck looks like a property access, not a function call. What's going on there?

I'll let Mark explain:

if it can't find a property called "otherWidgets", it uses PHP's magic __get, __set, and __call to create a call to getOtherWidgets.

Which, as we can see, getOtherWidgets calls into a WidgetPart, which also does the same magic, and calls its own getOtherWidgets.

class WidgetPart extends Base { public function getOtherWidgets() { $part = $this->name; $widgets = array(); $statuses = self::checkPartStatusForWidget($part,self::$widgetPartList); foreach($statuses[$widget] as $part=>$status){ $widgets[] = Widget::find($part)->self($w)->inline($w->status = $status); } return $widgets; } }

This starts out pretty normal. But this line has some oddness to it:

$widgets[] = Widget::find($part)->self($w)->inline($w->status = $status);

Okay, find makes sense; we're doing some sort of database lookup. What is self doing, though? Where did $w come from? What the heck is inline doing?

That's certainly what Mark wanted to know. But when Mark put in debugging code to try and interact with the $w variable, he got an undefined variable warning. It was time to look at the Base class.

class Base { // <snip> /** * Attach variable name to current object */ public function self(&$variable) { $variable = $this; return $this; } /** * Allows you to preform a statement and maintain scope chain */ ## Widget::find('myWidget')->self($widget)->inline(echo $widget->name)->part->... public function inline(){ return $this; } }

self accepts a variable by reference, and sets it equal to this, and then returns this.

inline doesn't do anything but return this.

Somehow, inline doesn't take parameters, but a statement in the parentheses gets evaluated. I can't accurately explain how this works. I can't even try getting these snippets to behave anything like this- clearly, there's more "magic" happening around the inline function to allow the inline execution of a statement, which Mark didn't provide.

Honestly, that's for the best- I'm not sure I want to see that. (Actually, I'd love to see that, but I'm a glutton for punishment)

But this is a whole lot of magic to allow us to play code golf. Without the magic, you could just… write a few lines.

$w = Widget::find($part); $w->status = $status;

You don't need to do any of this. It certainly doesn't make the code cleaner or easier to understand. And I certainly can't explain what the code is doing, which is always a problem.

It's the worst kind of code: clever code. May the programming gods save us from clever programmers.

.comment { border: none; } [Advertisement] Picking up NuGet is easy. Getting good at it takes time. ProGet costs less than half of Artifactory and is just as good. Our easy-to-read comparison page lays out the editions, features, and pricing of the different editions of ProGet and Artifactory.Learn More.
Categories: Computer

Error'd: Nothing Doing

Fri, 2024-09-13 08:30

Two this week from our old friend Michael R., or maybe it's one.

This week, Michael tells us "I am sure I am running some applications but it seems I am not and those not running have allocated all the memory."

 

Well, that seems fishy enough, except just a few days earlier, he sent us in this image as well, saying "Thank you for nothing, KDE." This makes me think his system just has an attitude problem.

 

Daniel D. shared a legitimate Error'd but I think he's taking it all too personally. Complains Daniel: "The first WTF is that tone - is there something wrong with you? Are you a robot? The second thing is: you are wrong!"

 

"I did percentage calculation wrong my whole life!" exclaims a surprised Basti . "It says up to 70% discount. Who wouldn't buy it then?!"

 

And finally, long-time lurker but only occasional contributor Philipp H. weighs in "I once registered the smart home device in their app on my phone, but what was my mail address I used back then? The App only shows me my "MI ID", so I started trying until I was rate-limited and redirected to ask hello@example.com for help. Maybe it is there way of telling me I should go to /dev/null." If this password dates back to the time of Philipp's first contribution here, it's no wonder it was gevergessen.

 

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

CodeSOD: Switching the Order

Thu, 2024-09-12 08:30

Let's say you had 6 different labels to display. There are many, many ways to solve this problem. Most of them are fine. Some of them are the dreaded loop-switch anti-pattern.

Julien unfortunately inherited one of those.

for($i=0;$i<=5;$i++) { switch($i) { case 1 : print('<p>Activé</p>'); break; case 0 : print('<p>Désactivé</p>'); break; case 2 : print('<p>En cours d\'inscription</p>'); break; case 3 : print('<p>En cours de désinscription</p>'); break; case 4 : print('<p>Désinscrit</p>'); break; case 5 : default : print('<p>Marqué invalide</p>'); break; } }

Indentation in the original.

What's beautiful in this, is that we can see that the order of the fields changed. Originally, it was meant to be "Activé", then "Désactivé". But for whatever reason, that wasn't what needed to be on the page, so they flipped the order… by changing the values in the case statement.

It's that bit there that really made this code special, to me. The out-of-order case is the annoying choice that makes bad code truly awful.

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

CodeSOD: Enumerated Science

Wed, 2024-09-11 08:30

As frequently discussed here, there are scientists who end up writing a fair bit of code, but they're not software engineers. This means that the code frequently solves the problem in front of them, it often has issues.

Nancy works in a lab with a slew of data scientists, and the code she has to handle gets… problematic.

index = 0 for index, fname in enumerate(img_list): data = np.load(img_list[index]) img = data[0][:,:] img_title 'img'+str(index).zfill(4)+'.jpg' cv2. imwrite(img_title, img) index = index + 1

This code takes a bunch of image data which had been serialized out as a NumPy array (e.g., just raw data). This code reads that with np.load, then writes it back out using cv2.imwrite, converting it to an image file.

In this tight little snippet, nearly everything is wrong.

We start with an enumerate(img_list). In Python, this returns a list of tuples- an enumerator value, and the actual value. Our for loop splits that back out into an index and fname variable.

We forget the fname variable exists, and get the filename again by doing an array indexing operation (img_list[index]).

We then slice the loaded array to extract just the dimensions containing image data, and that's fine. Then we generate a filename based on the index- which, it so happens, we already HAVE a good filename that we've opted not to use (in fname). That's not a WTF, it just annoys me- my usual path for these kinds of things is to read source data from one directory and dump converted data into a different directory with the same names. That's just me.

Then, for fun, we increment index. This, fortunately, doesn't break the loop, because we're setting index at the top of the loop, and thus the increment is overwritten.

This also tells us some history of the code. It originally didn't use enumerate, and just incremented the index manually. Someone mentioned, "You should use enumerate, it's more Pythonic," and so they added the enumerate call, without understanding what about it was more Pythonic.

As scientist written code goes, I wouldn't call this the worst I've seen. It annoys me, but at least I understand it, and it wouldn't be a huge effort to clean up.

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

CodeSOD: Unique Emails

Tue, 2024-09-10 08:30

Once upon a time, a company started a large React application. Like so many such projects, the requirements were vague and poorly understood, the management didn't have any sort of plan, and the developers didn't understand the framework. Once this inevitably started to go off the rails, all the developers were fired and then contractors were brought in, because that would be "cheaper".

Spoilers: it was not. So all the contractors were fired, and a new round of hires were brought in. This time, though, they'd correct their mistakes by strictly enforcing Scrum practices to be "agile".

This is where Gretchen entered the story.

The code was a mess, and every feature was fragile if not outright broken. The project was overbudget and behind schedule before Gretchen even started. In other words, things were going great.

Gretchen started with what appeared to be a simple task- fixing the fact that many text fields overflowed their bounding box and were thus unreadable. She dug in, and started going through the component code, which is where she found this:

const WorkspaceListItem = ({ address, client, primaryContact, phoneNumber, }) => { /** README: since from bulk invite primary contact proceed to the process even primary email address is empty, so I put in temporary email address 'hideme.ricblyz+@gmail.com', this temporary email should not display in user interface */ const clientName = primaryContact ? ( primaryContact.username.match(/hideme.ricblyz/g) ? ( <em>n/a</em> ) : ( primaryContact.username ) ) : ( <em>n/a</em> ) return ( <tr> {/* Lots of table columns constructed in a weird way, but that's not TRWTF */} </tr> ) }

Now, let's start with the comment. This component is used to handle bulk updates, and those bulk updates might be missing their email address. Now, the first problem here: email address is the username, and thus, is absolutely required. The idea that you can simply skip past this missing field by using a temporary value is just plain wrong. This is a hard requirement, and there absolutely is no requirement to accept entries without valid email addresses.

Worse, not pictured here, the temporary email address is hideme.ricblyz+UNIXTIMESTAMP@gmail.com- the Unix timestamp in seconds. The bulk operation, as you can imagine, does more than one row per second, meaning the "temporary" email addresses (that we don't need) are not unique.

At this point, does it even matter that the regex is technically wrong? The . matches any character, and while it's unlikely that someone has hidemeFricblyz as their email address, it would certainly cause unexpected behavior here.

Now, as it turns out, this code is only "fixing" the UI layer display of the missing email address. When the data is sent to the database, the username field is left as null, which causes the database to throw out errors- which is the correct behavior, despite what the UI thinks it's doing.

So, unnecessary code which doesn't do the right thing, and also contains a subtle bug, but the database schema ends up doing the right thing by accident anyway. And, for fun, this anti-pattern is spammed through 40-or-so React components.

As for Gretchen:

I'm not going to fix this now, after all it has the desired behavior in it's own way. And anyways, the sprint is already started and we're not allowed to change its scope.

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

CodeSOD: Take a Line Break

Mon, 2024-09-09 08:30

The number of web applications which need to generate PDFs is too damn high, in my experience. But it's a requirement which continues to exist for a variety of reasons, so we just have to accept it.

Derek was accepting of it, at least until he found this attempt to add ten lines of space between paragraphs.

var pgText2N2 = section.AddParagraph(); pgText2N2.Format.Alignment = ParagraphAlignment.Right; pgText2N2.Format.RightIndent = rightMargin; var pgText2N3 = section.AddParagraph(); pgText2N3.Format.Alignment = ParagraphAlignment.Right; pgText2N3.Format.RightIndent = rightMargin; var pgText2N4 = section.AddParagraph(); pgText2N4.Format.Alignment = ParagraphAlignment.Right; pgText2N4.Format.RightIndent = rightMargin; var pgText2N = section.AddParagraph(); pgText2N.Format.Alignment = ParagraphAlignment.Right; pgText2N.Format.RightIndent = rightMargin; var pgText3N = section.AddParagraph(); pgText3N.Format.Alignment = ParagraphAlignment.Right; pgText3N.Format.RightIndent = rightMargin; var pgText4N = section.AddParagraph(); pgText4N.Format.Alignment = ParagraphAlignment.Right; pgText4N.Format.RightIndent = rightMargin; var pgText5N = section.AddParagraph(); pgText5N.Format.Alignment = ParagraphAlignment.Right; pgText5N.Format.RightIndent = rightMargin; var pgText6N = section.AddParagraph(); pgText6N.Format.Alignment = ParagraphAlignment.Right; pgText6N.Format.RightIndent = rightMargin; var pgText7N = section.AddParagraph(); pgText7N.Format.Alignment = ParagraphAlignment.Right; pgText7N.Format.RightIndent = rightMargin; var pgText8N = section.AddParagraph(); pgText8N.Format.Alignment = ParagraphAlignment.Right; pgText8N.Format.RightIndent = rightMargin;

This falls into the common WTF of "hey, have you ever heard of a loop? Or an array?".

These variables are never actually used or referenced anywhere, which arguably makes their cryptic, meaningless names better, but… does it? Also, given that this is ten blank lines, do we actually need to set the alignment and indent on them? Maybe- I don't know this PDF generating library, but if so, that's it's own WTF. And while we're at it, I'm no expert in PDFs, but there's gotta be some way to just include space without spamming a bunch of paragraphs in the document.

At this point, though, I'd just take a loop.

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

Error'd: Home By Another Way

Fri, 2024-09-06 08:30

This week, we discover an answer to the question that has eternally plagued us: "is time travel possible?" I won't swear it's the right answer, but it's definitely an answer.

But first, Faroguy Chris P. both shared the same issue with us (if they are in fact different people and not just each other's sock puppet) Snarked one of them: "A previous Teams "What's New" pop-up was empty. At least this one has content, even if part of it is a broken image and visible header name."

 

And an anonymous but failed time-traveller regrets "It seems that Google Maps wants me to get on the bus 3 minutes ago. Good thing I remembered myself, but I don't think the notification would have made a difference."

 

Dipping his toe into the near past, Michael R. mused "The 70s tech and cuisine never ceases to surprise me." He is undoubtedly wondering how this technology survived nearly into the mid-21st century. How many years is "mid" anyway? https://thenudge.com/london-news/lahpet-larder/"

 

But he immediately figured out the answer, exclaiming "I knew it!" Or he will have known it, which is essentially the same thing.

 

Our recommendation to No One in Particular is that he should post a job notice of his own, and hire someone with experience. The world would be his oyster. But he just ponders without taking action: "So its 1pm on Monday 24th June in Melbourne\Australia (GMT +10) and im looking for a job. Im wondering if in the last 5 days i time travelled back in time to the 5th of June (to get my application in early), OR back on the 5th of June i travelled forward in time to the last 5 days (to apply for a job which didn't exist on the 5th)?"

 

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

CodeSOD: Strings go Asplodey

Thu, 2024-09-05 08:30

Anton has the joy of doing PHP work using one of the more popular e-commerce platforms.

Now, say what you will about PHP, its absolute mess of a root namespace has a function or class for just about everything. You want to split a string? We call that explode because that's way more radical than split. Want to join a string back together? We use implode, because that's the opposite of explode.

Or, if you forget how to use implode, you could always write it yourself, which is what Anton found in the codebase:

<?php $flag = false ?> <?php echo $this->__('Your order number is ') ?> <?php foreach ($_orderIds as $orderId=>$incrementId): ?> <?php if ($flag): ?> <?php echo ', ' ?> <?php endif; ?> <?php $flag = true ?> <a href="<?php echo $this->getViewOrderUrl($orderId) ?>"><?php echo $incrementId ?></a> <?php endforeach; ?>

Now, as "we reimplemented a built-in function" standards, this isn't the worst instance of that logic. The basic "for loop, with a flag to help you remember if you should add a comma or not," isn't absurd.

No, the real ugly I see here is the (ab)use of PHP's interpretation. Every statement is wrapped in its own <?php ?> block, which is not necessary, and certainly makes the code less readable. The whole (dubious) advantage of PHP is that you can mix code with literal HTML output. There's no reason to <?php echo ', '?> when we could just output a , and a space directly in the page. Or conversely, we could put most of the logic inside of a single <?php ?> block and echo as needed to generate output, since we're doing echos for pretty much everything anyway.

All in all, it's just ugly code. Widely used ugly code. (Or at least was widely used when Anton submitted this, which was some time ago- we could hope the code has been improved since then)

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

Lowering the Rent Floor

Wed, 2024-09-04 08:30

Things weren't looking good for IniOil. It was the 1980s in the US: greed was good, anti-trust laws had been literally Borked, and financialization and mergers were eating up the energy industry. IniOil was a small fish surrounded by much larger fish, and the larger fish were hungry.

Gordon was their primary IT person. He managed a farm of VAXes and other minicomputers, which geologists used to do complicated models to predict where oil might be found. In terms of utilization, the computer room was arguably the most efficient space in the company: those computers may have been expensive, but they were burning 24/7 to find more oil to extract.

The CEO sent out a memo. "Due to economic conditions," it read, "we are going to have to cut costs and streamline." Cutting costs and streamlining meant "hiring a bunch of Ivy League MBAs" who had a long list of reforms they wanted the company to adopt. One of them was to force all the various business units to use internal billing and charge other business units for their services.

At first, this looked like a good thing for Gordon. Their overhead costs were low- the IT team was small, and the VAXes were reliable, and the workloads were well understood. Billing out computer time was going to make all their metrics look amazing.

Unfortunately, facilities also was a billable unit. And they charged by square foot. Suddenly, the IT team was paying through the nose for the computer room.

What really stuck in Gordon's craw, however, was that it seemed like the computer room was getting billed at a more expensive rate than anything else in the building- it was about the same size as the sales floor, but was billed at double the rate.

Gordon raised this with facilities. "That's not true," they said. "We bill by the square foot. You've got twice as much square footage."

Gordon insisted that the computer room did not. He broke out a tape measure, went to the sales floor, took some measurements, then went to the computer room and repeated it. The difference was a matter of a few square feet.

Gordon went back to facilities. "Your measurements are wrong."

"They're square rooms," Gordon said. "How wrong could I be? A factor of two? Do you want to take the measurements?"

Facilities didn't need to take measurements. They had drawings. And the drawings showed a room that was 80'x80'… and was 12,800 sq ft. Gordon pointed out how that didn't make sense, by basic arithmetic, and the facilities manager tapped an annotation on the drawing. "Raised flooring".

Because the computer room had a raised floor, facilities was counting it as twice the floor space. Gordon tried to argue with facilities, pointing out that no matter how many raised floors were added, the actual building square footage did not change. But every business unit was looking to cut costs and boost internal profits, which meant "seeing reason" wasn't high on the facilities priority list.

Gordon raised it up with management, but everyone was too panicked by the threat of mergers and losing jobs to start a major fight over it. Facilities said the square footage was 12,800, then that's what it was. But Gordon's management change had a solution.

"Gordon," his boss said. "Remove the raised flooring. Just rip it out."

It was a quick and easy way to turn high billing rates into trip hazards and risks to equipment, but nobody was paying for risk mitigation and if there were any injuries because someone tripped over a cable, that'd come out of some other team's budget anyway.

For a few months, the computer room was a hazard site, but the rent was at least cheap. In the end, though, none of it mattered- all the MBA driven cost "savings" weren't enough to stop a much bigger oil company from swallowing up IniOil. Most of the employees lost their jobs. The execs who owned most of the shares got a huge payout. And, well, the purchaser was interested in land leases and mineral rights, not computer rooms, so the VAXes were broken down and sold to a few universities.

[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

Testing the Juniors

Tue, 2024-09-03 08:30

Stefan S has recently joined the ranks of software developers, having taken on his first job as a junior developer. After a few months of on-boarding with Terry, another new developer, they're now both actually getting assigned real work on tickets that deliver new functionality.

Stefan proudly pushed his first feature, complete with plenty of unit, functional, and end-to-end tests. After a little brushing up during code-review, it was merged along with a few "atta boys", and Stefan was feeling pretty good about himself.

A few days later, he pulled the latest changes, and ran the test suite. And all of the tests he wrote suddenly failed. Stefan's stomach dropped into his shoes, and he struggled to think: "How did I mess up this badly?"

Except Stefan didn't mess up that badly. A quick check on source control history showed that Terry had added some new commits- one of which "optimized" Stefan's code by adding a NullPointerException.

Stefan was relieved, but annoyed. He opted to, in his mind, "be a bro", and not open a ticket that the rest of the team could see, and instead messaged Terry directly. "Your changes have broken functionality. You need to fix it."

At 5:05PM, Terry pushed a fix, and messaged Stefan, "Tests don't fail anymore," then left for the weekend. Terry was correct, the tests stopped failing.

(Names anonymized by Stefan)

public class MyPerfectlyDoneTestClassNotReadyToHaveTheHunsReleasesUponItself { @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } @Test public void test(){ try { // lots of logic and setup 20+ lines } catch (Throwable throwable) { assertTrue(true); } } @Test public void test2(){ // different logic assertTrue(true); } @Test public void test3(){ try { // yet more well thought out logic. Since there was setUp to these tests // he had to move the setUp to the actual methods and since they throw he had to put them in try catch } catch () { assertFalse(false); } } @Test public void test40Plus(){ // I give up assertTrue(true); } }

So, yes. Terry just asserted true on any test that threw an exception. Or, asserted false. He also stripped out the set-up methods for the test suite and copy/pasted the setup code into methods. This massive change to the test suite was quite a bit of work- quite a bit more than just fixing the bug with the null pointer.

The lesson here, for Stefan, is that "being a bro" isn't a great idea. Opening a ticket and creating visibility isn't throwing Terry under the bus- Terry's a junior and these kinds of mistakes are still likely to happen, and any healthy organization will be willing to allow growth. When we see mistakes, we can correct them.

The additional visibility also helps the organization spot the deeper WTF: Terry's code should never have ended up in a branch anyone else was touching. CI and code review should have stopped Terry from breaking code in a way that impacted anyone else.

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

Best of…: Classic WTF: A Systematic Approach

Mon, 2024-09-02 07:30
It's a holiday in the US today, where we celebrate labor and laborers. Enjoy a story of working smarter, not harder, to meet unrealistic deadlines and create a lot more work for someone in the future. Original --Remy

It was the early 1990s and Frank was living the dream – unshaven, in pajama bottoms and his favorite hockey jersey, having just woken up at 12:18 PM, was now working in the dim light of his basement on one of his freelance projects. Just as he was sipping a cup of coffee, the phone rang.

Frank tried fruitlessly to fight an unexpected open-mouthed yawn when he picked up the receiver. "OOOOAAAaaahhhh... hello?"

"Hi, Frank. We have a very exciting opportunity for a qualified individual like you. Interested?" Odd that this caller hadn't even identified what company he was with, Frank thought. After sleepily getting some of the details, yeah, Frank was mildly interested, and he agreed to an interview in 30 minutes... at a bagel shop that was 25 minutes away.

Ten minutes late and having not changed at all aside from putting on non-pajama pants and shoes, he was immediately flagged over by a man in a nice suit. "You have the look like a computer genius, I already know you're perfect for this," he said without a hint of sarcasm. He asked lots of vague questions about computers; stuff like "So you know computers? What about, like, servers?" Frank had all the traits of a young man in IT – beyond his physical appearance, he was brash, overconfident, and narcissistic. Throughout the brief interview, he didn't restrain any of these traits, but still, he was only vaguely interested in the position, so he winged all the questions. Ten minutes later, the man pulled out a marker, wrote something on a napkin, and slid it across the table. "That's what your hourly rate would be." The man arched his eyebrow. "Interested?"

Frank was already pulling in some respectable bank, but this was more than twice what he was currently making. Gears started turning in his brain while he mulled it over. Well, I guess if I was to take this jo-"YES." On his way home from the interview, Frank picked up a razor, stopped for a haircut, and generally got himself kempt.

The Job

The day after the interview, he was sitting opposite the Executive Vice President of Data Processing, who was giving a second interview. "Let's say I asked you to consolidate twelve server facilities into one. How would you do it?" Ahh, a riddle question, like "how would you move a mountain to the other side of a village?"

"Well," Frank started, winging his second interview in as many days, "I'd take a systematic approach. Figure out where everything was, how the different systems were integrated, how they communicate, and gradually migrate servers over."

The Bank Boss was clearly impressed since Frank was given a contract on the spot; no references or background checks. And not just that, Frank was given keycard access to everything – all of the different datacenters and various facilities. He didn't encounter anything his keycard didn't give him access to (though he never tried the bank's vault). He was also given passwords to every sensitive system, including the money transfer system, responsible for transferring money in the range of around one trillion dollars every day.

It quickly became clear that the interview question wasn't a hypothetical, and Frank was dealing with the impossible. And maybe that's why they hired him – he was an idealistic hard worker, a badass who could do anything. What he lacked in experience then he'd make up for in tenacity. At least that's what Frank was having a harder and harder time reminding himself.

And for better or for worse, he'd effectively be on his own. As it turned out, his team and the man that hired him were electricians. Back in those days, the bank (and, more importantly, their union contract) considered moving servers to be "electrical work" and, therefore, only union electrician companies were allowed to do it. Or those that the electricians hired.

The Problem

In short, the bank had a dozen VAX datacenters that they wanted as a single cluster in a new building. Everything communicated over DECnet, which was a network protocol not too dissimilar from TCP/IP. One of the nice things about DECnet was the ability to route datagrams (packets) around the network in different ways to avoid failures, a fact which later proved to be useful.

The problem with the banks infrastructure was that either no one knew everything that was running in each datacenter, how it all worked, or how they'd work together in a single facility. Even worse, the people at the datacenters didn't want to talk to Frank, as the sooner Frank finished his job, the sooner they'd lose theirs.

The visits to the various datacenters always left Frank in a worse mood. All of them were in various states of disrepair – dirty, perhaps a flickering light, disorganized, but one stood above the others. Its floor was almost completely torn up due to a major renovation project following an asbestos contamination. All of the systems in this room were caked with dust, so much so that the labels were completely illegible. Only one system had lights blinking on its network port; so Frank brushed the dust off its label. "VDL-23 – Maintenance Test Box." Why does that sound so familiar?

Earlier that morning, Frank had done some network analysis, and found that all redundant links between the Money Transfer System and the other datacenters (aside from VDL-23) had been down for quite a while. Since DECnet routed around the failures and no one had thought to set-up network monitoring, no one had noticed that all of the other links had gone down. And before Frank discovered it, no one knew (or no one would tell him) where the box was physically. Immediately, Frank got cracking on a plan to bring the redundancy back online while keeping VDL-23 up, thanking the stars that the architecture of DECnet prevented the network from going down with all of the other servers. Still, something told him that the twisted, crushed, mistreated cables strewn across the floor might not remain reliable for transfers of $1T/day for long...

His watch alarm went off before he finished, it was a reminder that he had a mandatory meeting for all staff involved in the migration – about two hundred people in total. Frank rushed back to the main office, taking shortcuts using his everything-access keycard, and arriving just as the meeting was starting.

"Thank you all for coming," the executive vice president said, voice quivering, with a sweaty brow. "We're required to migrate the network to TCP/IP by the communications standard director." Frank winced.

The VP of communications chimed in, equally nervous. "Well... it's DECnet... it uses the non-routable LAT protocol. I... *ahem* we may need to push the deadline out..." The executive VP frowned, and asked for an update from the apps team.

The VP of Applications looked equally nervous, stammering out "we're still gathering information on the applications... and... we're not totally sure if we can actually run this all in one system..."

In a hushed tone, the executive VP took the floor again. "You're telling me we're going to miss our deadline. The board is very firm about our deadline. I'm not happy."

Frank craned his neck and looked around the room, everyone was staring at the floor. This was his moment. Once again feeling like a badass he rose from his seat dramatically, and in his most heroic voice, he boomed "There is a way."

Success!

Three months later, a beautifully appointed datacenter hummed along, processing transfers and perfectly replicating all of the functionality from the former twelve separate datacenters. Pristine rows of computers and neatly arranged drive controllers worked dutifully, everything in perfect order. The board came through the datacenter to inspect the work, and left with no complaints. It had been completed on schedule and was looking great, everyone was happy.

Under the raised floor, the tangle of cables told the real story. What was really in that room was the original twelve datacenters, all of the systems sitting pretty on the raised floor, underneath which were piles of cables connected exactly the same way they'd always been connected, and with back-hauled communication lines running between the systems. As they say, out with the old, in with the old.

And off in one corner, on upgraded hardware, was VDL-23; still responsible for routing all of the money transfer traffic, now safely sitting in a datacenter where it would remain unperturbed.

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

Pages