QL教程4-加冕合法继承人

前言

在我们使用codeql进行代码审计之前,不妨先学习一些QL的基础语法,磨刀不误砍柴工。

官方教程链接:https://codeql.github.com/docs/writing-codeql-queries/ql-tutorials/

在这个教程中我们作为一个侦探,为了解决遇到的问题使用QL进行相应的调查

加冕合法继承人

在这个QL侦探谜题中,你将会学会如何使用递归来编写更复杂的查询

巴西尔国王的继承人

害,在经历了一系列事件之后,村子里终于不再有犯罪--我们也终于可以离开村子回家了。

但我们在村子里的最后一晚,老国王--伟大的巴西尔国王--在睡梦中死去,到处都是混乱!

国王终生未婚,也没有孩子,所以没人知道谁应该继承国王的城堡和财产。随即,许多村民声称他们是国王的后裔,他们是真正的继承人,人们争论和战斗,似乎这样的情况没有解决办法。

最终我们决定留在村子里解决争论并找到真正的王位继承人。

你想知道村里是否有人真的与国王有关系,虽然看起来这是一项很艰巨的任务,但我们现在已经很了解村民了,而且我们拥有一个村里所有父母和他们孩子的名单,所以我们自信地开始工作。

要了解有关国王及其家人的更多信息,我们可以进入城堡并找到一些古老的家谱,另外我们将这些关系包含在QL数据库中,以查看国王家族中是否还有人活在世上。

以下谓词有助于我们访问数据:

例如,我们可以列出所有的孩子以及他们的父母

import tutorial
from Person p 
select parentOf(p) + " is a parent of "+p

手动搜索的信息太多了,所以我们还需要进一步优化查询来帮助我们找到国王的继承人。

我们知道国王自己没有孩子,但也许他有兄弟姐妹,编写QL查询

import tutorial
from Person p 
where parentOf(p)=parentOf("King Basil") and 
not p="King Basil"
select p


国王确实有兄弟姐妹!但是我们需要检查他们是否还活着...这时我们需要用到另外一个谓词isDeceased()

使用该谓词优化我们的查询,查看国王的任何兄弟姐妹是否还活着

import tutorial
from Person p 
where parentOf(p)=parentOf("King Basil") and 
not p="King Basil" and
not p.isDeceased()
select p


不幸的是,巴西尔国王的兄弟姐妹比他的寿命更短暂。是时候进一步调查了,childOf()定义一个返回该人孩子的谓词,该谓词可能对我们有所帮助。为此,我们可以在原有的查询中使用childOf()

Person childOf(Person p){
    p=parentOf(result)
}

将其用于我们的查询中

import tutorial
Person childOf(Person p){
   p=parentOf(result) 
}
from Person p 
where parentOf(p)=parentOf("King Basil") and 
not p="King Basil"
select childOf(p)

没有任何的结果,因此国王的兄弟姐妹们也没有子嗣,但或许巴西尔国王有一个堂兄在世?或者两个堂兄,或者...

情况变得越来越复杂,理想情况下,我们希望能定义一个谓词relativeOf(Person p来列出所有的亲戚

我们怎么样才能做到这一点?

有一个提示是:如果两个人有共同的祖先,则他们是相关的。

我们可以引入谓词ancestorOf(Person p)来列出某人的所有祖先,或者这个祖先的父母,或者祖先的父母的父母...不幸的是,这导致了无穷无尽的父母名单,而我们不知道什么时候才能结束,我们无法编写无限的QL查询,因此必须要有更简单的方法。

啊哈,通过思考你有了一个好点子,对于每一个祖先,他要么是当前查询的人的父母,要么是已经知道是祖先的人的父母,将其转换为QL为

Person ancestorOf(Person p){
    result=parentOf(p) or 
    result=parentOf(ancestorOf(p))
}

如代码所示,我们使用递归完成了这个查询。这种递归,其中多次应用相同的操作(即parentOf()),这在QL中非常常见,被称为操作的传递闭包,有两个特殊符号+*在使用传递闭包时非常有用。

  • parentOf+(p) : 将parentOf()谓词应用于p一次或多次,这相当于ancestorOf(p)
  • parentOf*(p) : 将parentOf()谓词应用于p零次或多次,因此它返回p或者p的祖先

尝试使用这个新符号来定义一个谓词relativeOf(),并用它来列出国王的所有在世亲属

提示:
这是其中一种定义relativeOf()的方法

Person relativeOf(Person p){
    parentOf*(result)=parentOf*(p)
}

添加上还活在世上的限制之后

import tutorial
Person relativeOf(Person p){
    parentOf*(result)=parentOf*(p)
}
from Person p 
where p=relativeOf("King Basil") and 
not p.isDeceased() 
select p


成功找到了两名国王的亲戚

选择真正的继承人

在下一次村民会议上,我们宣布有两个在世的国王亲戚。

为了决定应该由谁来继承国王的财产,村民们仔细地阅读了村章:

王位继承者是国王在世的最亲近的亲属,任何有犯罪记录的人都不会被考虑。如果有多个候选人,年龄最大的就是继承人。

作为我们的最后一个挑战,定义一个谓词hasCriminalRecord,以便在我们之前揭露的任何犯罪分子中成立(即前面的找到小偷和抓住纵火犯的教程中的罪犯)

import tutorial
Person relativeOf(Person p){
    parentOf*(result)=parentOf*(p)
}
predicate hasCriminalRecord(Person p) {
    p = "Hester" or
    p = "Hugh" or
    p = "Charlie"
}
from Person p 
where p=relativeOf("King Basil") and 
not p.isDeceased() and 
not hasCriminalRecord(p)
select p

好的, Clara!他就是最后的王位合法继承人!

实验探索

恭喜!我们已经找到了王位继承人并回复了村庄的和平。但是,我们暂时不必离开村民,我们还可以通过编写QL查询为村民回答一些关于村庄宪法的问题:

  • 哪个村民是下一个王位继承人?你能写一个谓词来确定剩下的村民与新君主的关系有多密切吗
  • 如果多个村民与君主有相同的关系,我们将如何使用QL查询选择最年长的候选人?

我们还可以尝试编写更多自己的QL查询来查找有关村民的有趣事实。你可以随意调查你喜欢的任何东西,但这里有一些建议:

  • 村里最常见的发色是什么?在每个地区
  • 哪个村民的孩子最多?谁的后代最多?
  • 村里每个地区有多少人
  • 所有村民都和他们的父母住在同一个村子里面吗
  • 看看村里面有没有时间旅行者!(提示:寻找"不可能的"家庭关系)

总结

到这里QL的语法我们就学习完毕了,虽然原链接后面还有一个过河问题,但我不认为这与QL语法有太大的关联,该问题更偏向于算法吧,如果后面做leetcode有机会遇到类似的问题,我会单独出一篇文章来讨论它

学完QL语法之后,我们就可以开始准备实战的工作了,后面的博文中会先从比较简单的小例子开始由浅入深逐步介绍codeql这款工具 😃

END

建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃

GIF GIF
posted @ 2022-01-16 16:21  春告鳥  阅读(169)  评论(0编辑  收藏  举报