近日,GitHub上线了一款利用人工智能生成模型来合成代码的工具——Copilot,但自发布以来一直饱受争议,包括版权纠纷和怪异的注释。并涉嫌抄袭。另外,生成的代码能不能用也是个大问题。本文Copilot测试邀请用户0xabad1dea在试用代码合成工具后发现了一些值得注意的安全问题,并据此编写了一份简单的风险评估报告。GitHub非常好,他们让我可以访问Copilotbeta,尽管我已经就ICE骚扰过他们数百次。这一次,我不关心Copilot的效率,我只想测试它的安全性。我想知道让AI为人编写代码的风险有多大。提交的每一行代码都需要人来负责,不能用AI来“清责”。副驾驶是一种工具,工具需要可靠才能发挥作用。木匠不必担心他的锤子突然坏了,导致建筑物的结构缺陷。同样,程序开发者也应该对工具有信心,而不用担心“搬石头砸自己的脚”。在Twitter上,我的一位追随者开玩笑说:“我迫不及待地想用Copilot编写代码。我希望它编写一个用于验证JSON网页令牌的函数,然后甚至不阅读它就可以提交它。”我沿着这条线使用了Copilot,得到了令人捧腹的结果:functionvalidateUserJWT(jwt:string):boolean{returntrue;}这可能是除了移除硬盘之外最糟糕的实现。这种错误是如此明显和粗暴,以至于任何专业的程序开发人员都不会反对。我更感兴趣的是,Copilot是否会生成乍看之下合理的代码,从而使其中的错误被程序员忽略或被经验不足的程序员认为是正确的。(剧透:这确实发生了。)我有意使用Copilot来生成实际上应该由人类编写的代码,因为用户肯定会在生产中这样做。UncertaintyCopilot是一个生成模型,这意味着它的目的是产生一个在统计上接近其输入(即训练数据)的输出。它的目标不是精确地复制它的输入,因为纯粹的复制不需要机器学习系统。这样的模型有一个通常称为“温度”的参数,它是一个可以在保守风格(模仿最常见的输入)和原始风格之间调整的滑动标尺。原创性设置越高,输出的结构化程度越低,您获得垃圾结果的可能性就越大。Copilot的温度由GitHub控制,据说他们会根据实验的当前阶段进行调整。以相同的方式两次调用生成模型通常不会两次产生相同的结果。它每次都重新运行其统计模型。这有它的好处——如果你不喜欢模特之前的建议,你可以再问一遍。这也减少了Copilot为不同用户生成相同代码的数量。然而,这不符合可靠性的最基本原则之一,即确定性。有可能某天完全按照您的示例文件所做的操作,而第二天却全错了。例如,使用完全相同的输入(一个仅包含注释、关键字“function”和一个空格的文件),我得到了以下两种不同的月相计算器实现:=Math.floor((newDate().getTime()-newDate().setHours(0,0,0,0))/86400000)%28;if(phase==0){return"NewMoon";}elseif(phase==1){return"WaxingCrescent";}elseif(phase==2){return"FirstQuarter";}elseif(phase==3){return"WaxingGibbous";}elseif(phase==4){return"FullMoon";}elseif(phase==5){return"WaningGibbous";}elseif(phase==6){return"LastQuarter";}elseif(phase==7){return"WaningCrescent";}}月相计算器B//返回月亮函数的当前相位getPhase(){varphase=Math.floor((newDate().getTime()-newDate(newDate().getFullYear(),0,0).getTime())/(1000*60*60*24))+1;if(phase>29){phase=29;}returnphase;}乍一看,他们似乎完成了同样的任务,但其中一个实现将一个月分为8个月phases,每个月相包含天数,然后返回日期对应的月相文本标签;另一个就像把每个月的每一天都当作不同的月相,没有文字标签。但实际上他们都做错了。计算器A说2021年7月2日是新月,而人工写的天文网站说2021年7月2日是残月。当相位值高得离谱(>29)时,计算器B将月相设置为29。两种实现乍一看似乎都有效,但是如果你比较一些结果,你会发现很难说哪一个是正确的。当我生成复活节日期计算器时,我实际上多次得到完全相同的输出,并且它是正确的(至少在我验证的某些年份)。我猜这意味着复活节计算器实现之间的差异比月相计算器之间的差异小得多。复活节计算器//calculatestheEasterdateforagiveyearfunctionEaster(y){vara=y%19;varb=Math.floor(y/100);varc=y%100;vard=Math.floor(b/4);vare=b%4;varf=Math.floor((b+8)/25);varg=Math.floor((b-f+1)/3);varh=(19*a+b-d-g+15)%30;vari=Math.floor(c/4);vark=c%4;varl=(32+2*e+2*i-h-k)%7;varm=Math.floor((a+11*h+22*l)/451);varn0=(h+l+7*m+114)varn=Math.floor(n0/31)-1;varp=n0%31+1;returnnewDate(y,n,p);}可解释性上面的复活节计算器是正确的,但我只是通过实验知道的;太难破译了。(更新:有人在评论中指出,有一个打字错误影响了少数年份——一个逃过我检查的错误!)Copilot可以而且有时确实会添加评论,但它在这里没有效果。里面的变量名也完全没用。我毫不怀疑其中一些是没有明确名称的中间结果,但总的来说它可以更清楚。有时,回到开头并从评论的开头调用会使Copilot尝试解释。例如,暗示//f在一个函数的中间会让Copilot声明//f是星期几(0=星期日),这似乎不正确,因为复活节星期日往往是在星期日。它还指出//代码来自http://www.codeproject.com/Articles/1114/Easter-Calculator,但这似乎不是真正的网站链接。Copilot生成的注释有时是正确的,但并不可靠。我尝试了一些与时间相关的功能,但只有这个复活节计算器是正确的。Copilot似乎很容易混淆用于计算日期的不同类型的数学公式。例如,由它生成的一个“GregoriantoJulian”转换器是混合在一起计算星期几的数学公式。即使是经验丰富的程序员也很难从统计上相似的代码中正确地辨别转换时间的数学公式。密钥和其他机密信息真正的加密密钥、API密钥、密码和其他机密信息永远不应在公共代码存储库中发布。GitHub会主动扫描这些密钥,如果检测到,则会向存储库持有者发出警告。我怀疑这个扫描仪检测到的所有东西都被排除在Copilot模型之外,这很难验证但肯定是有益的。这种类型的数据具有高熵(希望如此),因此像Copilot这样的模型很难在看到一次之后就完全记住它。如果您尝试从提示中生成它,Copilot通常会给出明显的占位符“1234”或一串十六进制字符-乍一看看起来是随机的,但基本上上面是交替的0-9和A-F。(不要故意用它来生成随机数。它们的语法是结构化的,Copilot可能会向其他人建议相同的数字。)但是,仍然可以使用Copilot恢复真实密钥,特别是如果您使用十个而不是单个打开窗格时的建议。例如,它为我提供了密钥36f18357be??4dbd77f050515c73fcf9f2,由于用于家庭作业,它在GitHub上出现了大约130次。在GitHub上出现100次以上的任何内容都不太可能是真正敏感的。最真实的风险是天真的程序员接受自动填充的密码作为加密密钥,使得结果值看起来随机但具有危险的低熵。从提示生成密码会产生各种有趣的不安全样本。这些样本通常用作训练数据中的占位符字符串。每个人最喜欢的占位符字符串是“mongoose”。生成脏话可能会给某些用户带来问题。证书清理GitHub已公开声明,他们在Copilot模型中包含了网站上托管的所有公共代码,无论证书如何。显然,他们认为这是合理使用,不受证书限制,但这种意见在法庭上是否站得住脚……还有待观察。可以很容易地验证Copilot是否包含GPL代码,因为Copilot可以很容易地从记忆中引用GPL证书文本。使用Copilot编写类似于具有独特命名约定的某些GPL项目的代码也很容易。关键是Copilot可以通过提示它对不需要的证书下的代码进行微小更改来用于“证书清理”。对于每个使用Copilot的人来说,这可能会或不会突然成为一个大的法律问题。安全漏洞示例:用C编写的HTML解析器一位朋友建议使用“带有正则表达式的通用HTML解析器”为Copilot提供提示,这恰好是您不应该做的示例;Copilot实际上拒绝使用正则表达式,而是编写了一个完整的C函数和一个像样的main()来驱动它。我所做的唯一修改是注释掉free(html),因为free()不是通过include定义的,并且在任何情况下都不需要。#include
