来自IT世界的好奇变态-4

图片

每日WTF网站已经收集了来自IT界的有趣,荒诞和/或悲伤的故事已有15年了。 我翻译了一些我觉得很有趣的故事。 所有公司名称和名称均已更改。 以前的问题可以在“ 好奇的变态 ”标签下找到。

第一个故事。 月底


[原创]

如果您询问设计工程师过桥是否安全,他将很乐意告诉您桥梁的可靠性,数学在桥梁中的工作原理,在建筑安全方面的进展。 与他交谈后,您会得到一个印象,那就是地球上不会再有一座桥梁会坍塌。 但是,如果您向软件开发工程师询问有关银行的信息,那么您很可能会感到恐惧,并且以50/50的概率说服自己将所有资金投资于比特币。 银行因创建软件时的错误决策而臭名昭著-不是因为这些决策令人作呕,而是因为大多数人都认为银行对安全性更加准确和谨慎。

加藤(Kato)在Inibank工作,在那里,一种名为T24的商业产品被用作银行系统的核心。 T24系统被全球数百家银行使用。 可以针对各种银行解决方案进行定制。 与大多数自定义程序包一样。 有一些专门为它编写代码的程序员,还有一些帮助银行进行重大更新的顾问。

Inibank员工很忙,因此该银行邀请了一名顾问参加一个特殊项目。 在工作日结束时,银行进行了完成过程,这是必要的,以便使所有款项都转到所指示的位置,重新计算所有必需的输出数据,并执行所有必需的报告。 在银行,此过程还将系统日期更改为下一个工作日。 这就是为什么当您在星期日进行网上银行业务时,直到星期一早晨才开始处理任何一笔交易。 顾问必须创建一个新报告,该报告将在完成过程中执行,并且如果日期对应于月底,则包括其他处理。

加藤向新顾问介绍了该银行如何设置日终报告。 “瞧,我们有上一个工作日,今天和下一个工作日的全局变量。 它们的格式为YYMMDD,以便于使用。

“是的,是的。 知道了 它们采用什么格式?”

“……呃……我只能假设这是年,月和日。”

“是的,是的,好的。 太好了 然后我开始工作。”

谈话之后,加藤对此感到不好。 但是他试图摆脱他。 顾问说,一切都准备就绪。 他完全知道自己在做什么,对吗? Kato从头顶上扔了下来,不再担心,直到代码审查的时间到了,他发现了一颗明珠:

 TH.DATE = R.DATES(EB.DAT.NEXT.WORKING.DAY)[1,6]:"01" CALL CDT('ES00',TH.DATE,"-1C") WTODAY = OCONV(DATE(),"DY") : FMT(OCONV(DATE(),"DM"),'R%2') : FMT(OCONV(DATE(),"DD"),'R%2') IF TH.DATE EQ WTODAY THEN 

简要解释一下这里发生的情况:

  1. 以下一个工作日并将日期更改为01,以获取该月的第一天。
  2. 我们通过在西班牙的日历中减去1个日历日来更改此日期。
  3. 我们从服务器获取日期并将其转换为YYYYMMDD格式,并三次调用Date命令。
  4. 如果在步骤2中计算的日期等于在步骤3中计算的日期,则开始该过程。

好吧,实际上...有效。 在大多数情况下。 除非出于某种原因,否则一天的倒数第二天倒数第二天才不会结束。 在这种情况下,代码将错误地认为这是该月的最后一天,并将开始创建报告。 这很好地解决了另一个问题:如果同一个月的最后一天发生了同样的事情,那么创建报告就不会错误地开始。 最好的错误:如果一个月的最后一天是星期天,则服务器的日历将永远不会安装在该日历上,因为它会跳过非工作日。

说到非工作日:由于Inibank位于美国,因此没有理由使用西班牙日历。 是的,月份和星期是相同的,但是在西班牙语软件日历中,您需要指定在美国的银行假期,否则该程序将继续运行。 最后,似乎还不够,三重日期调用意味着刚好在午夜开始时可能存在分歧:要求月值在午夜之前以及之后的第二天。

Kato添加了一条注释,提出了一种更改代码的方法:

 IF R.DATES(EB.DAT.TODAY)[5,2] # R.DATES(EB.DAT.LAST.WORKING.DAY)[5,2] THEN 

五分钟后,顾问去了他的办公桌。 “此编辑是什么意思?”

加藤当时不愿争论。 “您的密码已损坏,朋友。 所有这些都是不必要的。”

“我明白了,我明白了。 这实际上只是我们行业的标准操作程序。 好吧,好的。”

加藤对此表示怀疑,但只是耸了耸肩。 “那么这个行业是错误的。 我在评论中解释了所有内容。”

是的。 是的,我读了。 但我会再读一遍。” 然后他突然消失,消失了。

进行了编辑,加藤批准了守则,顾问消失了。

有时,加藤在晚上躺在床上,想知道:顾问真的了解自己做错了什么吗,还是只是同意看一下,收到了支票,然后继续写一个可怕的代码,价格是加藤的两倍? 在那些没有员工可以验证代码的银行中。

但是不要把所有的钱都投入到比特币上。 那里更糟。

第二个故事。 这是怎么做的


[原创]

人们喜欢吃热狗,直到发现如何做饭为止。 大多数人不问,因为他们不想知道并继续吃热狗。 在开发软件时,我们有时不得不问。 不仅要解决问题,还因为一些程序员担心他们的汽车中的软件以胶带和胶条组装而成,它们以100 km / h的速度沿着高速公路行驶。 我们整个行业的工作做得很差

Brett在MedStitute医学研究中心担任系统分析师。 MedStitute使用了称为MedTech的专有软件来存储和分析数据。 医生和研究人员都喜欢MedTech的结果,但是同事Brett Tyree知道它们是如何创建的。

该软件无权访问后端,并且整个开发过程都在“可编程鼠标” GUI中进行。 这个界面看起来像是一个人写的,他是从90年代开始通过复制粘贴网站研究编程的,然后看了十分钟的《侏罗纪公园》,并寻找StackOverflow的答案,直到可以编译某些东西为止。 “编程语言”在设计理念上也表现出相似的思想水平。 每个都必须有一个else 。 一些模块使用布尔值,其他模块返回空字符串以指示错误值。 从文档中,不清楚哪种情况发生在哪种情况下。 实际上,每个if变成了三个语句。

布雷特需要开始一项新的研究。 它基于一组简单的统计数据,并使用随机变量对患者进行分组。 布雷特(Brett)在列表中寻找可以随机化的变量,但认为没有必要。 他建议他犯了一个错误,并通过复制进行搜索以返回几个屏幕来检查她的名字。 Brett返回到随机变量列表。 她不在那里。 他仔细查看了该列表,并注意到随机变量列表包含来自多项选择字段的数据。 他需要随机分配的字段是基于计算得出的字段。

Brett知道Tyree正在从事另一个按计算场随机分配的项目,因此他在Slack与他联系。 “您如何编码此随机变量? Medtech是否可以阻止这种情况的发生?”

泰里写道:“我正在谈论会议,稍后我会给您回电。”

几分钟后,Tyree打电话给Brett。

“您需要从两个领域入手。 比方说 我们称它们$variable_choice (即多项选择题)和$variable_calced (即您的计算字段)。 当您要创建一个基于计算字段执行随机选择的变量时,您可以告诉Medtech该随机变量基于$variable_choice 。 然后删除$variable_choice ,并将$variable_calced重命名为$variable_choice

“停止,系统允许您执行此操作,但不允许您以任何其他方式随机化计算字段吗? 她不检查吗?”

“我希望一切都没有改变,直到我的项目完成,她才开始验证这一点,”泰瑞回答。

“这项研究应该持续十年。 它的成功完成取决于开发人员是否将此技巧视为错误?”

“我设法只找到了这样的解决方案。 让我知道您是否找到更好的选择。”

布雷特(Brett)不满意这样的破解,因此他重新研究了文档。 他找到了一个“更好”的解决方案:您可以使用唯一的默认值(即计算所得字段的值)创建一个只读的多选字段。 不幸的是,用户可能会计算计算字段的值之前通过回答多项选择题来无意中更改列表。

最后,布雷特剩下的唯一一件事就是休息一下,去自助餐厅买几个热狗。

第三个故事。 便携性和硬件


[原创]


许多个月前,当PC装有重金属和塑料外壳时, Matt和他的同事被要求为即将到来的销售部门运营评估软件包。 不幸的是,他和他的同事在同一城市的不同办公室工作。 在那个时代,还没有有效的在线协作工具,因此Matt经常不得不带着PC前往另一个办公室。 这意味着每次需要从机箱473断开外围电缆的连接,沿着走廊和楼梯将计算机携带起来,乘公共汽车去另一个办公室时,他以相反的顺序进行所有操作。 有时,不正确的劳动安排迫使这对夫妇在周末工作,这意味着他们将工作机器带回家。

在此过程中,Matt的计算机中的20 MB硬盘驱动器溢出。 从他的办公室,他向IT部门发送了一个请求。 为了完成该应用程序,任命了Gary的技术人员,该技术人员在一段时间后出现在Matt的立方体中,他拿着新的硬盘和螺丝刀。 加里(Gary)送马特(Matt)喝咖啡,专注于他的“病人”。 经过一番手术后,Matt的PC开机并使用大硬盘驱动器。

在项目截止日期的前一天,Matt几乎完成了自己的工作。 他只需要对报告进行一些补充,然后将其复制到软盘上并发送给销售部门。 将PC放回立方体并连接电线后,他打开电源并听到一声pop啪声。 PC已死,没有任何生命迹象。

在致电IT部门后,加里再次用螺丝刀出现在他的办公室。 他打开电脑机箱,立即尖叫:“等一下! 您是否将计算机拖到某处?”

马特皱了皱眉。 “嗯,是的。 那是东西吗?”

“是的,当然! 你不应该那样做!”加里开始诅咒。 “硬盘开始挂出并短路了里面的所有东西!”

马特俯身看加里,亲自看一下计算机内部。 他立即注意到新硬盘已“固定”到磁带上。

“停! 那不应该这样做!” 马特指着一块透明胶带。 “以防万一,我应该联系您的主管吗?”

加里的脸皱了皱眉。 “他们没有给我必要的紧固件!”

“然后找到拥有它们的人!”

鉴于迫在眉睫的最后期限,在老板的允许下,马特将他的申请书转更高了。 几乎立即,胶带被真正的硬件所取代。 他从来不理解为什么 IT部门的员工无法获得必要的设备; 为什么 ? 他建议节省金钱是一些白痴的绝妙主意。 Matt只能猜测还有其他绝望的即兴创作使他们的IT基础架构正常工作,以及如果PC不会损坏,它们会在多长时间内被忽略。

第四个故事。 这就是PL / SQL如何影响您的大脑。


[原创]

在最奇怪,最失败的决定中,永恒的冠军将永远是甲骨文 。 今天我们来看一些PL / SQL代码。

PL / SQL是一种奇怪的语言,它是SQL和语言(过程)语言(语言)的混合体,并且面向对象的内容粘在了一边。 语法极有能力给人以它是1970年代开发的印象,并且每种新功能或语言更改都延续了这一传统。

每个PL / SQL代码模块的结构都是基于的。 每个块都是一个独立的名称空间。 简而言之,他的解剖结构如下:

 DECLARE -- variable declarations go here BEGIN -- code goes here EXCEPTIONS -- exception handling code goes here, using WHEN clauses END; 

如果要编写存储过程或事件处理程序,则将DECLARE关键字替换为CREATE [OR REPLACE] 。 您还可以将块嵌套在其他块中,因此经常可以看到以这种方式构造的代码:

 BEGIN DECLARE --stuff BEGIN --actions END; --more actions END; 

是的,很快它开始变得混乱。 是的,如果您想至少提供近似结构化的错误处理,则必须开始将各个块放在一起。

语言和数据库还有其他有趣的功能。 在版本12c之前,它们没有IDENTITY列类型。 在以前的版本中,您必须使用SEQUENCE对象并编写执行强制自动编号的过程或事件处理程序。 通常,使用SELECT INTO…运算符将值分配给变量。 奖励:Oracle SQL 始终要求在FROM语句中指定一个表,因此您必须使用发明的dual表,如下所示:

 CREATE TRIGGER "SOME_TABLE_AUTONUMBER" BEFORE INSERT ON "SOME_TABLE" FOR EACH ROW BEGIN SELECT myseq.nextval INTO :new.id FROM dual; END; 

:new在这种情况下, :new表示我们要为其自动编号的行。 在旧版本的Oracle中,这是使用自动编号创建列的“常用”方法。 Benoit发现了执行相同操作的另一种不太常见的方法:

 CREATE OR REPLACE TRIGGER "SCHEMA1"."TABLE1_TRIGGER" BEFORE INSERT ON "SCHEMA1"."TABLE1" FOR EACH ROW BEGIN DECLARE pl_error_id table1.error_id%TYPE; CURSOR get_seq IS SELECT table1_seq.nextval FROM dual; BEGIN OPEN get_seq; FETCH get_seq INTO pl_error_id; IF get_seq%NOTFOUND THEN raise_application_error(-20001, 'Sequence TABLE1_SEQ does not exist'); CLOSE get_seq; END IF; CLOSE get_seq; :new.error_id := pl_error_id; END; END table1_trigger; 

这里发生了很多事情。 首先,请注意DECLARE节包含CURSOR语句。 游标允许您遍历记录。 它们非常昂贵,在Oracle世界中,这是一种需要释放的资源。

此事件处理程序(触发)无缘无故地使用嵌套块。 它还使用附加变量pl_error_id ,可以省去。

但是真正奇怪的是IF get_seq%NOTFOUNDIF get_seq%NOTFOUND 。 一切都很简单:它检查游标不返回字符串的条件。 对于此游标,即使从理论上讲也不会发生这种情况,因此永远不会执行内部操作。 序列始终返回一个值。 考虑到进一步的代码,这很好

raise_application_error与Oracle中的throw类似。 该语句在可执行块的堆栈中向上移动,直到找到EXCEPTIONS节来处理该错误为止。 请注意,我们此语句之后关闭了游标-实际上,我们从不关闭游标。 如上所述,游标很昂贵,Oracle仅允许使用数量有限的游标。

在这里,我们看到一个奇怪的示例,说明开发人员如何尝试防御无法发生的错误,这种错误会随着时间的推移导致新的错误。

第五个故事。 双重加密登录


[原创]

为Web API创建身份验证是一项艰巨的任务,但是它具有许多完善的解决方案。 实际上,最困难的事情是选择各种选项之一,然后仅添加一个组件就足够了。

正确实施后,系统将不依赖于客户端的类型。 我可以通过浏览器,胖客户端或cURL访问该服务。 如果实施不正确,您将了解Amira的情况

她解决了从后端提取所需统计信息的问题,但无法确定身份验证方法。 因此,她研究了前端代码以了解其如何执行身份验证:

 crypt = new JSEncrypt(); crypt.setPublicKey('<removed>'); challenge = "<removed>"; function doChallengeResponse() { document.loginForm.password.value.replace(/&/g, '%26'); document.loginForm.password.value.replace(/\\+/g, '%2B'); document.loginForm.password.value = crypt.encrypt(document.loginForm.password.value); document.loginForm.response.value = document.loginForm.password.value; document.loginForm.password.value = ''; document.loginForm.submit(); } 

一方面,考虑到用于与DOM元素进行交互的document.loginForm ,我可以假设这段代码很旧。 另一方面, JSEncrypt于2013年首次发布,为我们提供了最大的年龄限制。

我们通过提交表单将访问参数传递给后端,根据代码开发人员的要求,该表单需要进行清理-密码中的所有& +都将被替换,但是...这不是必需的,因为表单必须满足POST请求,此外, 我们还对数据进行了加密

这就是我的想法。 该代码实际上已经很旧了。 开发人员从2005年左右在StackOverflow上的帖子中复制了该帖子,该帖子未使用加密和通过POST表单。 年复一年,对其进行了小的更改。 但基本机制从未改变。

Amira检查了历史记录,发现以前的版本未使用加密。 challenge , MD5, , , .

: , , , , cURL -. , SSL/TLS .

Source: https://habr.com/ru/post/zh-CN446714/


All Articles