-Поиск по дневнику

Поиск сообщений в rss_thedaily_wtf

 -Подписка по e-mail

 

 -Постоянные читатели

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 06.04.2008
Записей:
Комментариев:
Написано: 0

The Daily WTF





Curious Perversions in Information Technology


Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://thedailywtf.com/.
Данный дневник сформирован из открытого RSS-источника по адресу http://syndication.thedailywtf.com/thedailywtf, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

CodeSOD: A Botched Escape

Понедельник, 04 Ноября 2019 г. 09:30 + в цитатник

Nancy was recently handed a pile of "modern" PHP that weighs in at tens of thousands of lines of code.

This is how every query is executed:

function getFoo($bar) { $bar = my_escape($bar); $sql = " select * from foo where bar = '" . $bar . "' "; return do_query($sql); }

Yes, this is a SQL injection vulnerability. No, there is no part of the application which uses parameterized queries. But wait, they call my_escape. That must be safely escaping the input so it can be used as a query param safely, right?

function my_escape($data) { if ( !isset($data)) { return ''; } if ( is_numeric($data) ) { return $data; } if(empty(trim($data))) { return ''; } $non_displayables = array( '/%0[0-8bcef]/', // url encoded 00-08, 11, 12, 14, 15 '/%1[0-9a-f]/', // url encoded 16-31 '/[\x00-\x08]/', // 00-08 '/\x0b/', // 11 '/\x0c/', // 12 '/[\x0e-\x1f]/' // 14-31 ); foreach ( $non_displayables as $regex ) { $data = preg_replace( $regex, '', $data ); } $data = str_replace("'", "''", $data ); return $data; }

So, this strips off url encoded characters, as well as hex-values. It, ah, doesn't strip off any of the characters which you might want to strip off if you're trying to protect against SQL injection. These regexes are case sensitive, too, so while %0b will get stripped, %0B won't. Which is also irrelevant, because by the time you're getting to passing things into the database, they've already been percent-decoded.

For extra fun, because the regexes are applied in a loop, it's possible that the regex might remove more than intended, because the first regex might strip characters which results in something that's picked up by a later regex.

For example, "A%%001%0cfB" turns into "A%1fB" after the first pass, then "AB" after the second one. But a slightly different string, like "A%%100%1cfB" turns into "A%0fB".

Now, those are contrived examples, but it's an interesting illustration of poorly thought out code. Looking at this, my guess is that someone knew they needed to sanitize their database inputs, but didn't know exactly how. They saw a my_escape function in the codebase, and said to themselves, "Ah, I can escape my inputs!" without checking what my_escape actually did.

This code has been running in production for a few years. Nancy would like to change how it works, but it's not currently "an organizational priority" and constitutes a "high risk change to a stable system".

[TDWTF Survey Reminder] Got 5 minutes to tell your manager what you really think? Take the Developer Mentorship Survey and there just might be a free TDWTF mug in it for you.

https://thedailywtf.com/articles/a-botched-escape


Метки:  

Error'd: Airport via TCP

Пятница, 01 Ноября 2019 г. 09:30 + в цитатник

Peter G. writes, "Luggage from flight SQ978 arriving from Singapore on belt 12. Luggage from PQ968 arriving from Ko Samui on belt 6. Packets from VNC arriving from Kazakhstan on port 5900"

 

"HTML 503 errors can be annoying, but are they REALLY breaking news?" Rob H. wrote.

 

Joachim I. writes, "Azure Devops sure wants me to cancel something here. (So I just closed the browser and hoped for the best.)

 

"Kudos to whoever thought about future proofing their job application form!" Jeff C. writes.

 

"It's refreshing to see a rewards program that's so honest about the value of the points you earn," Paul V. wrote.

 

"Thanks to the Android SDK, I guess it's time to spring for a new SSD," wrote Akseli A.

 

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

https://thedailywtf.com/articles/airport-via-tcp


Метки:  

What Lives Beyond the Blue Screen (2019)

Четверг, 31 Октября 2019 г. 11:00 + в цитатник

As promised in the sneak peak, we have a very special Halloween feature planned for today! What Lives Beyond the Blue Screen is an animated story by Lorne Kates (voiced by Jack Rhysider), made in collaboration with our new friends at Human Readable Magazine:



An everyday programmer decides to clean up the mess of his company's infrastructure before the big merger only to accidentally run the wrong command on the wrong location. Join the adventure as they rush to fix the mistake before they bring down the entire company.

This will be you live-streamed premier, in just a few hours, at 1:00PM Eastern. Stay tuned!

About Human Readable Magazine

We’re able to bring this to you thanks to Panagiotis “Pek” Peikidis. In addition to having an even more Greek name than mine, Pek is embarking on a remarkably awesome – and a bit insane – journey. He’s launching Human Readable Magazine: an actual paper magazine (available digitally too) that will take developers on technical deep dives to expand and challenge their knowledge of programming every month.

It’s a kickstarter project that you should all go check out. Pek was inspired to start this after finding a lot of success with Morning Cup of Coding, a newsletter that shares programming articles from every field of programming.

Creating quality content isn’t easy, but from what I’ve seen from Issue 0 Preview, and the creativity in the upcoming Halloween collaboration, Pek is up for the challenge. With our support, we can help make Human Readable Magazine a fixture of the programming community and have a lot more fun collaborations in the future.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

https://thedailywtf.com/articles/what-lives-beyond-the-blue-screen-2019


Метки:  

CodeSOD: Tern Down Service

Среда, 30 Октября 2019 г. 09:30 + в цитатник

In C, it’s not uncommon to define a macro like this one:

#define MIN(a,b) (a>b?b:a)

It’s useful to be able to quickly find the smallest of two numbers, and it’s useful to do that with something a bit more readable than a ternary.

Of course, if you need to expand this to larger sets of numbers, it gets tricky. For example, maybe you need to find the smallest of three numbers.

Agripina recently had to track down some strange behaviors in an IoT device, and found this stack of ternaries:

int lowestVal(int a, int b, int c){
    return a > b > c ? c : b ? b > c ? c : b : a;
}

Three question marks is the mark of a great ternary mangling. The complete lack of any parentheses to group the expression to provide any sense of the actual logical flow is also great.

It’s also entirely wrong.

This expression: a > b > c is a valid C expression, but it doesn’t do what the mathematician who wrote this may have expected. The > returns 0 if the comparison fails, and 1 if it’s true. Thus, if a = 10; b = 9; c = 2;, a > b results in 1, and 1 > 2 is also false, which results in 0.

This bug created all sorts of strange behaviors in the IoT firmware, and created a fair number of billable hours for Agripina in tracking down the source.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

https://thedailywtf.com/articles/tern-down-service


Метки:  

Once Bitten, Twice Tested

Вторник, 29 Октября 2019 г. 09:30 + в цитатник

Blake had recently been hired as a software tester, tasked with testing the company's product on the latest operating system, Windows 2000. After running through his battery of tests, he informed management that he hadn't encountered any issues, and the product was dubbed Windows 2000-ready. During the next several weeks, the product was smoothly deployed by customers—until an installer bug report came in.

"Did you test the desktop shortcut after installing on Windows 2000?" Blake's manager, Sammy, asked from the threshold of Blake's cube.

"Yeah, I'm sure I did," Blake replied.

"A customer emailed us to say that when he chooses to add the desktop shortcut while installing, it causes a Blue Screen of Death," Sammy explained. "It happens consistently for him. The only way he can install successfully is to not choose the desktop shortcut option, which he calls 'unacceptable from an IT security standpoint.'"

Blake frowned in confusion. "Security?"

"I know, it's weird," Sammy said. "I want to question him further on that point. In the meantime, I'd like for you to start looking into this."

The first step was to reproduce the problem in-house. Blake was sure he would fail; he was absolutely certain that he'd already tested what was allegedly crashing. His first move was to install the product on a fresh Windows 2000 box. He checked the "Add a desktop shortcut" option, and after a few moments, the installer completed with no errors. A shortcut to run the program now sat on the desktop. Double-clicking the icon opened the program flawlessly.

From there, Blake uninstalled and reinstalled the program. Yet again, no issues. Trying a different Windows 2000 PC was fruitless. Out of desperation, he even tried installing on Windows 98. In no case did a BSOD ever occur.

"I can't reproduce this bug," Blake told Sammy the next time the two were able to meet up in the latter's office. "The installer doesn't crash the system, and the desktop shortcut works fine. Is there something I'm missing?"

"Well, I just got some more info from the customer," Sammy said, with world-weariness bearing down upon him. "Did you try installing to the desktop?"

"Yes, I installed the desktop shortcut. Many, many times."

"No, not the shortcut. I mean, install to the desktop."

"To the desktop?" Blake repeated, frowning.

"The customer's corporate security policy considers program shortcuts untrustworthy. They fear they could be pointing to anything," Sammy explained. "To avoid any sort of issue along those lines, the customer is required to install all of his programs into separate folders on the desktop."

Blake's jaw fell. "What?!"

Sammy shrugged helplessly. "First they want to install shortcuts, now they don't trust shortcuts. I don't get it, either, but it doesn't matter. It should be possible to install our software into any valid folder without a BSOD. Go see if you can dupe this now."

Blake slinked back to his desk. Much to his chagrin, he was able to reproduce the crash. Upon further investigation, it was discovered that the installer could crash the OS if one tried to install to a new folder on the desktop or in the user's Documents folder. Blake received a scolding from Sammy for missing this the first time around.

It was a lesson well learned. In the years that followed, Blake strove to test every possible scenario, every fringe use case, every baffling type of input data that he could think of. Upon submitting his bug reports, he heard the occasional bemused comment from the developers: "Who in the real world would ever do that?"

Blake would merely chuckle to himself, remembering the desktop shortcut.

[TDWTF Survey Reminder] Don't miss your chance to tell managers what you REALLY think about good (and WTF-worthy) dev mentorship. You might win a TDWTF mug for participating!

https://thedailywtf.com/articles/once-bitten-twice-tested


Метки:  

CodeSOD: To Be Random Enough

Понедельник, 28 Октября 2019 г. 09:30 + в цитатник

A long time ago, when I was first learning about databases, one of the points brought up was the difference between a "natural key" and a "surrogate key". A natural key was a unique identifier which already existed in your dataset, and surrogate keys were those you made up- UUIDs or sequences or what have you.

As a best practice, even if you have a viable natural key, you should still use a surrogate key. There are exceptions, but it's usually preferable to employ a database key which you control to provide identity, especially one which has no meaning- because that means it'll never need to change values.

Adam H's co-worker never got this memo.

They needed to store data about vehicles. The particular data being stored, in this case, was also time series data. So they decided that their key would be a mashup of the timestamp and the vehicle's 6-digit license plate number.

There were a number of problems with this: not every record entered into this table was actually tied directly to a vehicle, so sometimes the license plate number was blank. The developer needed to avoid any key collisions, so they decided to randomly generate a value, which was hopefully "random enough".

public final class TransformationUtil { private TransformationUtil(){} //snip private static final Random rand = new Random(); public static String setUniqKey(LocationHistory locationHistory){ SimpleDateFormat date = new SimpleDateFormat("yyyyMMddhhmmssSSSSSS"); Calendar c = Calendar.getInstance(); String str = date.format(c.getTime()); if (locationHistory.getVehicleNumber() != null) { str = (str).trim().concat(locationHistory.getVehicleNumber().trim()); } else { String value = uniqKey(); str = (str).trim().concat(value.trim()); } if (str.length() > 30) { locationHistory.setUniqKey(str.substring(0, 30)); } else { locationHistory.setUniqKey(str); } } public static String uniqKey() String randomNumber; ArrayList numbers = new ArrayList(); for(int i = 0; i < 999999; i++) { numbers.add(i + 1); } Collections.shuffle(numbers); String randomNum2 = numbers.get(rand.nextInt(numbers.size())).toString(); if(randomNum2.length() ==2){randomNumber = "0000" + randomNum2;}else if(randomNum2.length() ==1){randomNumber = "00000" + randomNum2 ;}else if(randomNum2.length() ==3){randomNumber = "000" + randomNum2 ;}else if(randomNum2.length() ==4){randomNumber = "00" + randomNum2 ;} else if(randomNum2.length() ==5){randomNumber = "0" + randomNum2;}else{randomNumber = randomNum2;} return randomNumber; } }

setUniqKey is almost not terrible. Get a date, format the timestamp. If you have a vehicle number, whack it onto the end. If the result is longer than 30 characters (because not every vehicle number is 6 characters), just chop off the excess and hope that it's unique enough.

But if you don't have a vehicle number, you need to invent one randomly. There are a lot of ways to randomly generate numbers, and this program certainly uses one of them.

The uniqKey method fills an array with every number from 1 to 999999. It then shuffles that array. Then, it randomly picks a position out of the shuffled array. Finally, it pads the array using that delightful chain of if-statements to fill the length. In a choice explicitly made to drive me nuts, the ==1 condition comes after the ==2 condition, because of course it does.

A fair number of records getting inserted needed a randomly generated key. Given that this is an incredibly inefficient way to generate random numbers, this whole block didn't perform very well. The input data was coming from a JMS queue, and any time there was a run of no-vehicle-ID records, the queue would back up and cause a series of cascading failures in other components. Each time that happened, the ops team would "solve" it by spinning up a new VM with the component running on it. More and more instances of just this component appeared, trying to keep up with the load.

Adam suggested, "If you're not going to just use a UUID for a key, why not just switch to Random.nextInt?"

"That won't be random enough," the developer replied.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

https://thedailywtf.com/articles/to-be-random-enough


Метки:  

Error'd: Errors on the Go!

Пятница, 25 Октября 2019 г. 09:30 + в цитатник

"These adertisers are getting smarter. Picture ads of 'one weird trick doctors hate people are doing in Yourcity, USA' are on the outs. On the other hand...Mysterious blocks of JavaScript? You have my attention...," Angela A. writes.

 

Quentin wrote, "How am able to Board the plane 12 hours and 41 minutes after it has left? Where's that damn time travelling phone booth when I need it?"

 

"Wait...How exactly can you can define something when define is not defined?" Martin P. wrote.

 

Mike R. wrote, "Know what would be great? What if Google has some way to lock a field so it couldn’t be edited"

 

"I'm very excited to finally install... er, i... Z... wha?" George writes.

 

Bellons write, "Google Calendar wants to do something...I just need to have faith (or not) that I pick the right one."

 

[TDWTF Survey Reminder] Don't miss your chance to tell managers what you REALLY think about good (and WTF-worthy) dev mentorship. You might win a TDWTF mug for participating!

https://thedailywtf.com/articles/errors-on-the-go


Метки:  

Announcements: Sneak Peak: What Lives Beyond the Blue Screen

Четверг, 24 Октября 2019 г. 18:00 + в цитатник

I’m totally stoked for what we have brewing for Halloween. It’s called What Lives Beyond the Blue Screen, and we’ve got a fun sneak peak for you:



We’re able to bring this to you thanks to Panagiotis “Pek” Peikidis. In addition to having an even more Greek name than mine, Pek is embarking on a remarkably awesome – and a bit insane – journey. He’s launching Human Readable Magazine: an actual paper magazine (available digitally too) that will take developers on technical deep dives to expand and challenge their knowledge of programming every month.

It’s a kickstarter project that you should all go check out. Pek was inspired to start this after finding a lot of success with Morning Cup of Coding, a newsletter that shares programming articles from every field of programming.

Creating quality content isn’t easy, but from what I’ve seen from Issue 0 Preview, and the creativity in the upcoming Halloween collaboration, Pek is up for the challenge. With our support, we can help make Human Readable Magazine a fixture of the programming community and have a lot more fun collaborations in the future.

In the mean time, stay tuned… What Lives Beyond the Blue Screen is coming next week!

https://thedailywtf.com/articles/sneak-peak-what-lives-beyond-the-blue-screen


Метки:  

CodeSOD: Enumerating Your Failures

Четверг, 24 Октября 2019 г. 09:30 + в цитатник

Rick was recently looking at some code from another team at his company. He noticed something odd in the code, so he pinged the team lead, Linda. “Did you spot this?”

“Oh, crap no,” Linda replied. “I should have caught this in code review, but I gotta be honest, Teddy is a bit… well, let’s just say I really should have caught that since I knew it was a Teddy commit.”

Rick didn’t know much about Teddy, beside the fact that Teddy had a few years of experience. Certainly, he had enough experience to be familiar with Java programming, like working with enums and working with unit tests. Unfortunately, he doesn’t seem to understand them.

public class ProductCodeEnumTest
{
  @Test
  public void testProductCodeEnum_ProductCodeEnumExists()
  {
    String[] actualResults = {"PRODUCT_CODE_A","PRODUCT_CODE_B"};
    ProductCodeEnum[] expectedResults = ProductCodeEnum.values();
    for (int i=0; icode>

This unit test confirms that the ProductCodeEnum starts with PRODUCT_CODE_A and PRODUCT_CODE_B. Is that a useful test? Of course not! Did Teddy write a test like this for every enum in the code base? Well, he tried to, but Linda usually caught them in code reviews.

Maybe Teddy wanted to make sure other developers didn’t change the enum, but this isn’t the way to do it, and also, why would you worry about preventing changes to the enumerated type? If another developer removed a used entry, you’d get a compile time error. If a developer adds a new entry, it probably belongs there. If another developer changes the order of the entries, that shouldn’t matter at all. None of this matters.

I suppose this makes their code coverage metrics look better, at least.

Rick supplies his own enumeration of Teddy’s failures, and predicts the comments on this article:

Looking forward to reader comments regarding:
* the pointlessness of unit tests of enums (or the old sweats pointing out the couple of valid edge cases)
* the reversed names of actual vs expected results
* the useless message in assertEquals
* the fact that the test requires the enum to be in a certain order to pass
* the fact that the test will still pass if extra values are added to the enum
* the oddly double-up class name (yes it really is named in the format testEnumName_EnumNameExists)
Are there any other code smells I missed?

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

https://thedailywtf.com/articles/enumerating-your-failures


Метки:  

Announcements: First Annual Developer Mentorship Survey

Среда, 23 Октября 2019 г. 11:30 + в цитатник

Nearly fifteen years ago, I wrote Pounding A Nail: Old Shoe or Glass Bottle?. It opened by asking how one might respond to the following question:

A client has asked me to build and install a custom shelving system. I'm at the point where I need to nail it, but I'm not sure what to use to pound the nails in. Should I use an old shoe or a glass bottle?

The parallel to software development was that so-called professionals would ask questions just as preposterous in "the newsgroups/forums/lists" (i.e. the pre-StackOverflow days). And even worse, the answers given by other professionals may as well be step-by-step instructions for how to pound finishing nails using a half-empty bottle of Jack Daniels.

This didn't seem right to me at the time. I wondered if being a "grumpy curmudgeon" by berating them for asking such an absurd question would work. Perhaps, it'd be the form of shock therapy they'd need to set them on the right path? That's what worked for me, anyways.

I learned from my menor, Joe CELKO

--CELKO-- Joe CELKO was, and still is awesome. He's a database legend, and wrote a series of database books that should be mandatory reading for all software developers.

CELKO is also a very scary man. But the right kind of scary... exactly the way that an Internet mentor you've never gotten a chance to meet should be like.

There is an old fanblog dedicated to some of CELKO's greatest hits, which range from genius-level SQL answers to... well.... harsh.... answers to frankly stupid question. For example:

>> What is the proper syntax for stacking these IFs in a Stored Procedure

What the hell are you talking about?? IF-THEN-ELSE-ENDIF constructs in any 
procedural programming language are nested or sequential. This has nothing to 
with SQL. Don't you know how to program in ANY language??!! 

Please post something that makes sense when you are sober 

--CELKO--

To me, watching someone with godlike SQL abilities scold the unworthy masses was inspiring. And also hilarious, in a dunce cap sort of way. Getting scolded is a rite-of-passage, I had believed, and the "victims" of scoldings walk away with war stories and ultimately become better people.

Although I was never a victim of a CELKO scolding, I still feared the man. But it was the right kind of fear... that CELKO would somehow, using whatever black magic database gods possess, see terrible code I wrote and unleash a scathing-yet-hilarious critique. To this day, I still live by the database motto WW--CELKO--D. As should you.

I became a much better developer thanks to CELKO. All of the data integrity, performance, and maintainability problems that CELKO warned were very real; I saw them countless times at countless clients. But they never impacted any of the systems I built, because I had a good mentor. CELKO saved me and my team countless headaches and problems.

Not everyone learns like me

In the many years since I wrote that article, I've come to realize that different developers have different capabilities, different skills, and different learning styles. Nearly all developers want to do a great job, but most don't even know that they don't know what they're doing. While someone like CELKO does indeed have all of the answers, they aren't the right mentor for most people.

One of the most important responsibilities that a development manager has is to navigate the differences on their team, and help everyone learn. Whether that's transferable skills like C# or SQL, or institutional knowledge that lets them succeed within the company. This isn't easy to do, and most managers -- like their team -- are ill-equipped to do their job.

But I also believe managers want to do a great job, and I think I've got one way to help: provide them what different developers want to learn and how they want to learn it. This is where you come in.

The Developer Mentorship Survey... win fabulous prizes!

I've put together a survey with questions on a number of topics:

  • Code Review; are these used as a learning opportunity
  • Technology Skills; how you learn skills like C#, SQL Server, JavaScript, etc.
  • Institutional Knowledge; who guides you within the company and systems
  • Technical Debt; how bad do things look on the inside, and are they fixing it
  • Understanding the Mission; how well you see how your code adds value to the business
  • Development Process; your understanding and usage of the overall software process

Your answers will help paint a picture of not only different learning styles and ambitions, but how those vary in different kinds of organizations. I'll analyze this data, review other management studies, apply my many years of experience studying curious perversions in information technology, and compile everything in an easy-to-digest report. And then I hope to continue doing this year after year, refining and improving on the process along the way.

And did I mention... win valuable prizes? I'll randomly be selecting at least 25 respondents who entered an email address to win a free The Daily WTF mug. I'll also pick out some respondents who left some great/insightful comments as well.

Take the Developer Mentorship Survey »

Please spread this survey far and wide! I'd like to get as many developers as possible to take this survey, so please ask your coworkers, developers friends, and random peple on the internet with your social media network.

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

https://thedailywtf.com/articles/first-annual-developer-mentorship-survey


Метки:  

Counting on Common Sense

Среда, 23 Октября 2019 г. 09:30 + в цитатник

Matt enjoyed teaching. He mentored junior devs and interns with no technological backgrounds, and helped them to be experienced programmers. He believed that employers should hire based on attitude and then train employees up on skills.

That was before he met Derrick.

Matt wasn’t entirely clear on how Derrick became an junior dev at their company. Maybe he was pushed by his parents or grandparents to get into that “new tech field”, or maybe he was someone’s son, or a family friend of an executive.

To say that Derrick lacks technical experience is an understatement. Matt’s seen Amish farmers at the local market with a better understanding of programming. He proudly uses an antique flip phone, because that way the government can’t track him. The company uses two factor authentication via an app, and bought him a phone which supports the app. He leaves the phone at his desk and only uses it to log in. He writes code in Notepad where possible, because it’s installed on every computer and anything else is just excess.

Worst, Derrick was mostly good natured and wanted to help, but didn’t understand why he needed mentoring and guidance.

Most recently, Matt was trying to introduce Derrick to the life of being “full stack”, which is to say- getting data from the back end and building a report on the front end. In this case, it was a marketing report for tracking clicks related to a marketing campaign.

It’s been rough going. While reviewing a recent changeset, Matt spotted a problem and messaged Derrick on Slack. “Hey, Derrick, that new module you just delivered? You added a bunch of count fields, but you’ve made them all text fields. Can you change them to be numeric? They’re counts, so they can’t be anything else.”

“Sure, Matt,” Derrick replied, “but hey, not to get down on your mentoring, but you really ought to specify these things precisely.”

Matt nearly choked on his coffee, internally screaming, “are you serious?” Matt cursed to himself and then gave himself 15 minutes to cool down, before replying back.

“Sorry, Derrick,” Matt typed, “but I forget you may not be aware of the standards that are employed…” Matt typed everywhere, but decided to be more subtle, and replaced it with, “…here. But usually, if something is a type of number, we keep it as that numeric type. Sometimes this is an integer, a float, a decimal. If you have any questions, feel free to ask me. For now, can you change the text field to integers? We are counting clicks, so those are integers.”

“Yeah, okay,” Derrick responded. “I’ll make them numbers.”

An hour later, Derrick submitted another changeset.

“Hey, Derrick,” Matt messaged again, “Those count fields you set as numbers. They seem to be floating point numbers. Could you change them to integers? They’re counts. Some of the summaries are showing rounding errors- 4.99999997, etc.”

“Yeah, sure thing, Matt,” Derrick replied. “But hey, you really ought to specify these things precisely.”

In a fit of restraint, Matt did not quote his earlier message requesting that Derrick use integers. Matt knew he could count on many things, but the one thing he couldn’t count on was Derrick.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

https://thedailywtf.com/articles/counting-on-common-sense


Метки:  

CodeSOD: A Select System Call

Вторник, 22 Октября 2019 г. 09:30 + в цитатник

Way back in the 90s, in an era when Swedish pop bands were getting regular US radio play and CVS was the optimal source control system, Alan worked on a remote-execution service for a “Unix-like” OS. One of his co-workers had just left the company, and Alan needed to track down a bug in a module which the co-worker had more-or-less owned during their tenure.

The specific block of C code in question looked roughly like this:

    int nsel, pty_fd, net_fd;
    fd_set rdset;
    .  .  .
    FD_ZERO(&rdset);
    FD_SET(pty_fd, &rdset);
    FD_SET(net_fd, &rdset);
    nsel = select(max(pty_fd, net_fd), &rdset, NULL, NULL, NULL);

Now, select is a system call, and it’s a bit of a weird one, and Alan assumed that was where the root issue lied. FD_SET is a built-in macro which adds a file descriptor to a set, so in this example, we expect to place two file descriptors into our set.

We then use select to monitor those two file descriptors, so that we can get input from whichever one has bytes available.

At a glance, this code just looks wrong. The first parameter to select is the number of file descriptors. Passing max(pty_fd, net_fd) implies that we’re passing whichever file descriptor just happens to be the larger number, which makes no sense.

So whatever weirdness in the code Alan was observing, that must be the root cause, right? Well, max is the root cause, but not in the way you think. One year ago, Alan’s co-worker had placed this into source control:

#define max(x, y)   16  /* ((x) > (y) ? (x) : (y)) */

While you can get away with this sort of thing when providing a truly random number, you can’t do it when computing a max.

Alan adds:

There’s no real shame in having one’s work tree in that state while learning the (um … less-than-intuitive) interface to “select()”, but committing the code as is to CVS and leaving the company in good standing over a year later would probably prompt some blushing today.

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

https://thedailywtf.com/articles/a-select-system-call


Метки:  

CodeSOD: How Would You Rate Your Experience

Понедельник, 21 Октября 2019 г. 09:30 + в цитатник

The last time I was traveling, I noticed a little stand just past the security checkpoint, asking me to rate my experience. The five buttons ranged from a red frowny face to a green smiley, giving me the ability to respond with what must be the full range of human emotion. Every time I see one of those kiosks, I have to wonder: who uses those things, and who actually looks at the data?

Perhaps inspired by that kiosk, Peter's company does something similar. There's a Slack bot which messages each employee every two weeks to get them to evaluate, on a scale of 1-5, how good a certain aspect of their job is. That data gets collected up onto a Google Sheets dashboard. And yes, the Google Sheet is both the datastore and the dashboard, which isn't surprising at all.

Now, if you're using a regular old SQL database, you know that you shouldn't manually construct SQL strings, and instead use prepared statements, etc. to manipulate the data. But what if a spreadsheet is your database? Well, those rules go out the window, as this Scala block demonstrates:

def statsSheet(year: Int): Vector[Vector[String]] = { def monthStats(question: Int): Vector[String] = Vector.tabulate(numberOfPeriods(year))(month => s"=IFERROR(AVERAGE(FILTER('$year'!${weekToColumn(month)}$$2:${weekToColumn(month)}; $$A${2 + question} = '$year'!$$C$$2:$$C)))") def questionStats: Vector[Vector[String]] = questions.zipWithIndex.map { case (question, index) => val averagesByPeriod = monthStats(index) Vector(question) ++ averagesByPeriod ++ Vector(s"""=IFERROR(AVERAGE(INDIRECT("RC[-${averagesByPeriod.length}]:RC[-1]", false)))""") }.toVector val average: Vector[Vector[String]] = Vector( Vector("Average") ++ Vector.fill(numberOfPeriods(year) + 1)(s"""=IFERROR(AVERAGE(INDIRECT("R[-${questions.size}]C:R[-1]C"; false)))""") ) Vector(Vector("Question") ++ Vector.tabulate(numberOfPeriods(year))(weekToDates(year, _)) ++ Vector("Year")) ++ questionStats ++ average }

This block of Scala code builds the statistics sheet, and as you can see, it directly constructs formulas to inject into Google Sheets. That alone is pretty ugly and awful, but there's a more subtle problem here.

This Slack bot messages employees every two weeks. Well, that monthStats method isn't aggregating stats across an entire month- it's doing the stats across individual periods, which are two weeks long. So the month variable which gets passed to the weekToColumn method holds an integer representing the individual two week period, not the month.

Now, it's easy to say: "this is just a silly bot so who cares?" But users love dashboards. Especially pointy-haired-bosses. This employee survey has key performance indicators tied to the results, HR and management goals for employee satisfaction depend on those, and a variety of new policies crop up any time certain questions start drifting below an average of 4.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

https://thedailywtf.com/articles/how-would-you-rate-your-experience


Метки:  

Error'd: Encoded for YOUR Benefit

Пятница, 18 Октября 2019 г. 09:30 + в цитатник

"Oracle makes it easy! Just dereference this pointer to view your failed invoice," wrote Jeremy W.

 

"Oh no! It's too late for Computing. They weren't able to fend off evolving threats in time!" writes David B.

 

Jean R. wrote, "Well, thank you Google for this handy tip! I'm sure it is helpful for any cyborg out there that might want to do some mental arithmetic."

 

"While this Microsoft mouse is light on CPU and storage, it makes up for it by being so small and lightweight!" Dave L. writes.

 

Andrzej wrote, "Ah yes, undefined, undefined, and undefined...truly some of my favorite artists. WAAAAY better than null."

 

Bob E. writes, "Keep this up and it'll be free Ham and Cheese Benedicts for everyone!"

 

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/encoded-for-your-benefit


Метки:  

CodeSOD: A Context for Logging

Четверг, 17 Октября 2019 г. 09:30 + в цитатник

When logging in Java, especially frameworks like Spring, making sure the logging statement has access to the full context of the operation in flight is important. Instead of spamming piles of logging statements in your business logic, you can use a “mapped diagnostic context” to cache useful bits of information during an operation, such that any logging statement can access it.

One of the tools for this is the “Mapped Data Context”, MDC. Essentially, it’s very much like a great big hash map that happens to be thread-local and is meant to be used by the logging framework. It’s a global-ish variable, but without the worst side effects of being global.

And you know people just love to use global variables.

Lothar was trying to figure out some weird requests coming out of an API, and needed to know where certain session ID values were coming from. There are a lot of “correct” ways to store session information in your Java Spring applications, and he assumed that was how they were storing those things. Lothar was wrong.

He provided this anonymized/generalized example of how pretty much every one of their REST request methods looked:

 @Override
   public Wtf getWtf(String wtfId) {

    Map params = new HashMap<>();
    params.put("wtfId", wtfId);
    params.put("sessId", MDC.get(MDC_LABEL_SESSION_ID));
    params.put(MDC_LABEL_SESSION_ID, MDC.get(MDC_LABEL_SESSION_ID));

    UriComponents uriComponents = UriComponentsBuilder
            .fromUriString("https://thedailywtf.com")
            .buildAndExpand(params);
    String urlString = uriComponents.toUriString();
        ResponseEntity responseEntity = restTemplate.getForEntity(urlString, byte[].class);
  }

Throughout their application, they (ab)used their logging framework as a thread-local storage system for passing user session data around.

Sure, the code was stupid, but the worst part about this code was that it worked. It did everything it needed to do, and it also meant that all of their log messages had rich context which made it easier to diagnose issues.

If it’s stupid and it works, that means you ship it.

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

https://thedailywtf.com/articles/a-context-for-logging


Метки:  

CodeSOD: The Replacements

Среда, 16 Октября 2019 г. 09:30 + в цитатник

Nobody wants to have a Bobby Tables moment in their database. So we need to to sanitize our inputs. Ted C noticed a bunch of stored procedures which contained lines like this:

  @scrubbed = fn_ScrubInput(fn_ScrubInput(@input))

Obviously, they wanted to be super careful, and make sure their inputs were clean. But it got Ted curious, so he checked out how the function was implemented. The function body had one line, the RETURN line, which looked like this:

  RETURN REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@input, '"', '"'), 
'*', '\*'),'~', '\~'), '@', '\@'), '#', 
'\#'), '$','\$'),'%','\%'),'^','\^'),
'&','\&'),'(','\('),')','\)'),
'_','\_'),'+','\+'),'=','\='),'>',
'\>'),'<','\<'),'?','\?'),'/',
'\/')

Whitespace added.

Ted REPLACE REPLACE REPLACEd this with a call to the built-in STRING_ESCAPE function, which handled the escaping they needed.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/the-replacements


Метки:  

CodeSOD: Cast Away

Вторник, 15 Октября 2019 г. 09:30 + в цитатник

The accountants at Gary's company had a problem: sometimes, when they wanted to check the price to ship a carton of product, that price was zero. No one had, as of yet, actually shipped product for free, but they needed to understand why certain cartons were showing up as having zero cost.

The table which tracks this, CartonFee, has three fields: ID, Carton, and Cost. Carton names are unique, and things like 12x3x6, or Box1, or even Large box. So, given a carton name, it should be pretty easy to update the cost, yes? The stored procedure which does this, spQuickBooks_UpdateCartonCost should be pretty simple.

ALTER PROCEDURE [dbo].[spQuickBooks_UpdateCartonCost] @Carton varchar(100), @Fee decimal(6,2) AS BEGIN DECLARE @Cost decimal(8,3) = LEFT(CAST(CAST((CAST(@Fee AS NUMERIC(36,3))/140) * 100 AS NUMERIC(36,3)) AS VARCHAR), LEN(CAST(CAST((CAST(@Fee AS NUMERIC(36,3))/140) * 100 AS NUMERIC(36,3)) AS VARCHAR)) - 1) + CASE WHEN RIGHT(LEFT(CAST(CAST((CAST(@Fee AS NUMERIC(36,3))/140) * 100 AS NUMERIC(36,4)) AS VARCHAR), LEN(CAST(CAST((CAST(@Fee AS NUMERIC(36,3))/140) * 100 AS NUMERIC(36,4)) AS VARCHAR)) - 1), 1) > 5 THEN '5' ELSE '0' END IF NOT EXISTS (SELECT 1 FROM CartonFee WHERE Carton = @Carton) BEGIN INSERT INTO CartonFee VALUES (@Carton, @Cost) END ELSE BEGIN UPDATE CartonFee SET Cost = @Cost WHERE Carton = @Carton END END

Just stare at that chain of casts for a moment. It teeters on the verge of making sense, calls to LEFT and RIGHT and multiplying by 100- we're just doing string munging to round off, that must be what's going on. If I count the parentheses, and really sit down and sketch this out, I can figure out what's going on, it must make sense, right?

And then you spot the /140. Divide by 140. Why? Why that very specific number? Is it a secret code? Is it a signal to the Illuminated Seers of Bavaria such that they know the stars are right and they may leave Aghartha to sit upon the Throne of the World? After all, 1 + 4 + 0 is five, and as we know, the law of fives is never wrong.

As it turns out, this stored procedure wasn't the problem. While it looks like it's responsible for updating the cost field, it's never actually called anywhere. It was, at one point, but it caused so much confusion that the users just started updating the table by hand. Somebody thought they'd get clever and use an UPDATE statement and messed up.

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

https://thedailywtf.com/articles/cast-away


Метки:  

CodeSOD: I See What Happened

Понедельник, 14 Октября 2019 г. 09:30 + в цитатник

Graham picked up a ticket regarding their password system. It seemed that several users had tried to put in a perfectly valid password, according to the rules, but it was rejected.

Graham's first step was to attempt to replicate on his own, but couldn't do it. So he followed up with one of the end users, and got them to reveal the password they had tried to use. That allowed him to trigger the bug, so he dug into the debugger to find the root cause.

private static final String UPPERCASE_LETTERS = "ABDEFGHIJKLMNOPQRSTUVWXYZ"; private int countMatches(String string, String charList) { int count = 0; for (char c : charList.toCharArray()) { count += StringUtils.countMatches(string, String.valueOf(c)); } return count; }

This isn't a great solution, but it at least works. Well, it "works" if you are able to remember how to recite the alphabet. If you look closely, you can tell that there are no pirate on their development team, because while pirates are fond of the letter "R", their first love will always be the "C".

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/i-see-what-happened


Метки:  

Error'd: The WTF Experience

Пятница, 11 Октября 2019 г. 09:30 + в цитатник

"As it turns out, they've actually been singing Purple Haze before the start of all of those sportsball games," Adam writes.

 

Andrew C. writes, "When you buy from 'Best Pool Supplies', make no mistake...you're going to pay for that level of quality."

 

Jared wrote, "Pulling invalid data is forgiveable, but using a loop is not."

 

"VMware ESXi seems a little confused about how power state transitions work," writes Paul N.

 

"At first I was annoyed I didn't get the job, but now I really want to go in for free and fix their systems for them!" Mark wrote.

 

Peter M. writes, "Oh yes, Verizon! I am very excited! ...I'm just having a difficult time defining why."

 

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

https://thedailywtf.com/articles/the-wtf-experience


Метки:  

CodeSOD: Parse, Parse Again

Четверг, 10 Октября 2019 г. 09:30 + в цитатник

Sometimes, a block of terrible code exists for a good reason. Usually, it exists because someone was lazy or incompetent, which while not a good reason, at least makes sense. Sometimes, it exists for a stupid reason.

Janet’s company recently bought another company, and now the new company had to be integrated into their IT operations. One of the little, tiny, minuscule known-issues in the new company’s system was that their logging was mis-configured. Instead of putting a new-line after each logging message, it put only a single space.

That tiny problem was a little bit larger, as each log message was a JSON object. The whole point of logging out a single JSON document per line was that it would be easy to parse/understand the log messages, but since they were all on a single line, it was impossible to just do that.

The developers at the acquired company were left with a choice: they could fix the glitch in the logging system so that it output a newline after each message, or they could just live with this. For some reason, they decided to live with it, and they came up with this solution for parsing the log files:

def parse(string):
  obs = []
  j = ""
  for c in string.split():
    j += c
    try:
      obs.append(json.loads(j))
      j = ""
    except ValueError:
      pass
 
  return obs

This splits the string on spaces. Then, for each substring, it tries to parse it as a JSON object. If it succeeds, great. If it throws an exception, append the next substring to this one, and then try parsing again. Repeat until we’ve built a valid JSON document, than clear out the accumulator and repeat the process for all the rest of the messages. Eventually, return all the log messages parsed as JSON.

As a fun side effect, .split is going to throw the spaces away, so when they j += c, if your log message looked like:

{"type": "Error", "message": "Unable to parse JSON document"}

After parsing that into JSON, the message becomes UnabletoparseJSONdocument.

But at least they didn’t have to solve than newline bug.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/parse-parse-again


Метки:  

Поиск сообщений в rss_thedaily_wtf
Страницы: 124 ... 84 83 [82] 81 80 ..
.. 1 Календарь