go语言模板注入攻击 go语言注解

go依赖注入dig包使用-来自uber公司原文链接:
github:
Dependency Injection is the idea that your components (usuallystructsin go) should receive their dependencies when being created. This runs counter to the associated anti-pattern of components building their own dependencies during initialization. Let’s look at an example.
Suppose you have aServerstruct that requires aConfigstruct to implement its behavior. One way to do this would be for theServerto build its ownConfigduring initialization.
This seems convenient. Our caller doesn’t have to be aware that ourServereven needs access toConfig . This is all hidden from the user of our function.
However, there are some disadvantages. First of all, if we want to change the way ourConfigis built, we’ll have to change all the places that call the building code. Suppose, for example, ourbuildMyConfigSomehowfunction now needs an argument. Every call site would need access to that argument and would need to pass it into the building function.
Also, it gets really tricky to mock the behavior of ourConfig . We’ll somehow have to reach inside of ourNewfunction to monkey with the creation ofConfig .
Here’s the DI way to do it:
Now the creation of ourServeris decoupled from the creation of theConfig . We can use whatever logic we want to create theConfigand then pass the resulting data to ourNewfunction.
Furthermore, ifConfigis an interface, this gives us an easy route to mocking. We can pass anything we want intoNewas long as it implements our interface. This makes testing ourServerwith mock implementations ofConfigsimple.
The main downside is that it’s a pain to have to manually create theConfigbefore we can create theServer . We’ve created a dependency graph here – we must create ourConfigfirst because ofServerdepends on it. In real applications these dependency graphs can become very large and this leads to complicated logic for building all of the components your application needs to do its job.
This is where DI frameworks can help. A DI framework generally provides two pieces of functionality:
A DI framework generally builds a graph based on the “providers” you tell it about and determines how to build your objects. This is very hard to understand in the abstract, so let’s walk through a moderately-sized example.
We’re going to be reviewing the code for an HTTP server that delivers a JSON response when a client makes aGETrequest to/people . We’ll review the code piece by piece. For simplicity sake, it all lives in the same package ( main ). Please don’t do this in real Go applications. Full code for this example can be foundhere .
First, let’s look at ourPersonstruct. It has no behavior save for some JSON tags.
APersonhas anId ,NameandAge . That’s it.
Next let’s look at ourConfig . Similar toPerson , it has no dependencies. UnlikePerson , we will provide a constructor.
Enabledtells us if our application should return real data.DatabasePathtells us where our database lives (we’re using sqlite).Porttells us the port on which we’ll be running our server.
Here’s the function we’ll use to open our database connection. It relies on ourConfigand returns a*sql.DB .
Next we’ll look at ourPersonRepository . This struct will be responsible for fetching people from our database and deserializing those database results into properPersonstructs.
PersonRepositoryrequires a database connection to be built. It exposes a single function calledFindAllthat uses our database connection to return a list ofPersonstructs representing the data in our database.
To provide a layer between our HTTP server and thePersonRepository , we’ll create aPersonService .
OurPersonServicerelies on both theConfigand thePersonRepository . It exposes a function calledFindAllthat conditionally calls thePersonRepositoryif the application is enabled.
Finally, we’ve got ourServer . This is responsible for running an HTTP server and delegating the appropriate requests to ourPersonService .
TheServeris dependent on thePersonServiceand theConfig .
Ok, we know all the components of our system. Now how the hell do we actually initialize them and start our system?
First, let’s write ourmain()function the old fashioned way.
First, we create ourConfig . Then, using theConfig , we create our database connection. From there we can create ourPersonRepositorywhich allows us to create ourPersonService . Finally, we can use this to create ourServerand run it.
Phew, that was complicated. Worse, as our application becomes more complicated, ourmainwill continue to grow in complexity. Every time we add a new dependency to any of our components, we’ll have to reflect that dependency with ordering and logic in themainfunction to build that component.
As you might have guessed, a Dependency Injection framework can help us solve this problem. Let’s examine how.
The term “container” is often used in DI frameworks to describe the thing into which you add “providers” and out of which you ask for fully-build objects. Thediglibrary gives us theProvidefunction for adding providers and theInvokefunction for retrieving fully-built objects out of the container.
First, we build a new container.
Now we can add new providers. To do so, we call theProvidefunction on the container. It takes a single argument: a function. This function can have any number of arguments (representing the dependencies of the component to be created) and one or two return values (representing the component that the function provides and optionally an error).
The above code says “I provide aConfigtype to the container. In order to build it, I don’t need anything else.” Now that we’ve shown the container how to build aConfigtype, we can use this to build other types.
This code says “I provide a*sql.DBtype to the container. In order to build it, I need aConfig . I may also optionally return an error.”
In both of these cases, we’re being more verbose than necessary. Because we already haveNewConfigandConnectDatabasefunctions defined, we can use them directly as providers for the container.
Now, we can ask the container to give us a fully-built component for any of the types we’ve provided. We do so using theInvokefunction. TheInvokefunction takes a single argument – a function with any number of arguments. The arguments to the function are the types we’d like the container to build for us.
The container does some really smart stuff. Here’s what happens:
That’s a lot of work the container is doing for us. In fact, it’s doing even more. The container is smart enough to build one, and only one, instance of each type provided. That means we’ll never accidentally create a second database connection if we’re using it in multiple places (say multiple repositories).
Now that we know how thedigcontainer works, let’s use it to build a better main.
The only thing we haven’t seen before here is theerrorreturn value fromInvoke . If any provider used byInvokereturns an error, our call toInvokewill halt and that error will be returned.
Even though this example is small, it should be easy to see some of the benefits of this approach over our “standard” main. These benefits become even more obvious as our application grows larger.
One of the most important benefits is the decoupling of the creation of our components from the creation of their dependencies. Say, for example, that ourPersonRepositorynow needs access to theConfig . All we have to do is change ourNewPersonRepositoryconstructor to include theConfigas an argument. Nothing else in our code changes.
Other large benefits are lack of global state, lack of calls toinit(dependencies are created lazily when needed and only created once, obviating the need for error-proneinitsetup) and ease of testing for individual components. Imagine creating your container in your tests and asking for a fully-build object to test. Or, create an object with mock implementations of all dependencies. All of these are much easier with the DI approach.
I believe Dependency Injection helps build more robust and testable applications. This is especially true as these applications grow in size. Go is well suited to building large applications and has a great DI tool indig . I believe the Go community should embrace DI and use it in far more applications.
打造自己的渗透测试框架—溯光 TrackRay简介
溯光 , 英文名“TrackRay” , 意为逆光而行,追溯光源 。同时致敬安全圈前辈开发go语言模板注入攻击的“溯雪”,“流光” 。
溯光是一个开源go语言模板注入攻击的插件化渗透测试框架,框架自身实现go语言模板注入攻击了漏洞扫描功能,集成了知名安全工具:Metasploit、Nmap、Sqlmap、AWVS等 。
溯光使用 Java 编写,SpringBoot 作为基础框架,JPAHSQLDB嵌入式数据库做持久化,Maven 管理依赖 , Jython 实现 Python 插件调用 , quartz 做任务调度,freemarkerthymeleaf 做视图层,Websocket 实现命令行式插件交互 。
框架可扩展性高,支持 Java、Python、JSON 等方式编写插件,有“漏洞扫描插件”、“爬虫插件”、“MVC插件”、“内部插件”、“无交互插件”和“可交互插件” 等插件类型 。
功能展示
主页
登录
任务创建
任务列表
任务详情
无交互接口插件调用
MVC插件示例
交互式插件控制台
MSF 控制台
依赖环境
JDK 1.8
Python 2.7
Maven
Git
Metasploit
Nmap(建议安装)
SQLMAP(建议安装)
AWVS
**加粗为必须环境,没有安装程序则无法正常编译运行
不论是 Windows 还是 linux 一定需要先安装 JDK1.8 和 Maven 。安装过程这里不做演示 。保证 JDK 和 Maven 都在系统环境变量,能执行java -version 和 mvn --version即可 。
安装过程
第一步
手动启动 AWVS 服务
登录后台,生成一个API密匙 。
复制密匙和 AWVS 的地址 。
找到web/src/main/resources/application.properties配置文件 。
修改如下部分
第二步
找到你 python 的第三方库目录 。
Windows 的一般在 python 安装目录下的/Lib/site-packages
Linux 下可以通过输出 sys.path 来找第三方包路径
我的是 D:/Python2/Lib/site-packages
同样找到web/src/main/resources/application.properties配置文件 。
修改python.package.path参数
第三步
安装 Maven 后找到仓库位置 。
如果没有在 settings.xml 里配置指定仓库目录,默认会在当前用户目录中生成一个 .m2的目录
找到仓库目录后修改 application.properties 的 maven.repository.path参数
第四步
这个是 DNSLOG 回显检测漏洞时需要的 。
去 ceye.io 注册一个账号,拿到给你分配的域名和 TOKEN 。
修改配置文件
第五步
启动 msf 和 sqlmapapi 。
如果你是 kali 操作系统 , 可以直接运行startdep.sh 。
如果是其他系统,则要找到 metasploit 和 sqlmap 的目录分别执行
启动成功后修改配置文件
第六步
编译打包程序
等待依赖下载完成和编译完成,如果以上操作都没有出现问题则会提示 BUILD SUCCESS
编译成功后会在当前目录打包一个trackray.jar就是溯光的主程序 。
然后直接执行startup.bat或startup.sh溯光就会启动服务 。
没有抛出异常或ERROR日志 , 访问 8080 端口正常 。
服务启动正常后 , 登录 iZone 社区账号 。
**开发插件建议使用 Intellij IDEA IDE,需要安装 lombok 插件 。
目录结构
插件
AbstractPlugin
这是交互式插件和非交互式插件的父类 。
BASE常量
其中的静态常量 BASE 是 /resources/include/ 的所在目录 。
如果你的插件需要额外的静态资源,那么你可以在 /resources/include 目录里创建一个和插件 KEY 相同的文件夹,便于识别,如果没有在 @Plugin 注解中设置 value 则默认的插件 KEY 就是当前类名首字母小写 。
如 Typecho001 = typecho001
check(Map param)
这是用于检验是否合规的方法,需要被强制重写,当返回 true 时才会调用 start() 方法
param 参数是从前台传过来的参数键值对 。
常被用于检验参数格式是否正确或漏洞是否存在 。
after()
在 start() 方法之前调用
before()
在 start() 方法之后调用
start()
这是一个抽象方法,所有继承了该类的子类都需要重写这个方法 。
在check 方法 通过后会调用 start() 方法
start() 方法返回值最终会会当做插件结果,响应给前台 。
shell()
调用当前系统 shell 来辅助完成插件功能 。
executor()
插件执行的主方法
crawlerPage
http请求对象(不推荐使用)
fetcher
执行 http 请求对象(不推荐使用)
errorMsg
当校验不通过时,返回给前台的信息 。
param
前台传过来的参数键值对
requests
HTTP 发包工具(推荐使用)
hackKit
hack 常用工具包
无交互插件
无交互插件需要你填写好所有要填写的参数,直接请求接口来执行插件 。
默认需要去继承 CommonPlugin类 。
这是一个抽象类,继承了 AbstractPlugin
主要多出来两个属性:request 和 response 。
继承了 CommonPlugin 的类可以通过调用这两个属性来控制请求和响应内容 。
无交互插件同时也需要使用 @Rule 和 @Plugin 插件,这两个注解后面会讲到 。
在 ,找到相应的插件填写好参数提交即可完成调用 。
或直接调用接口 。
交互式插件
交互式插件一般在命令行控制台中调用,可以允许你通过命令行交互来完成插件的调用 。
交互式插件由 Websocket 实现,想要写一个交互式插件,首先要继承 WebSocketPlugin 类 。
同时设置 @Rule 注解的 websocket 参数为 true ,如果需要异步交互需要将 sync 也设置为 true 。
内部插件
内部插件是不可以通过外部去调用的,需要继承 InnerPlugin 并使用 @Plugin 注解 , 通常在漏洞扫描时时会调用 。
例如 “网页爬虫”,“指纹识别”,“端口扫描” 等,都是通过调用内部插件实现的 。
还有用于检测 SSRF 等漏洞用的 FuckCeye 插件也属于内部插件 。
通过 spring 的自动注入,来注入内部插件到当前对象 。
例子可参考 WebLogicWLSRCE.java
爬虫插件
爬虫插件会在扫描任务被勾选“网页爬虫”时调用 , 每爬取一条请求就会调用一次爬虫插件 。
爬虫插件需要继承 CrawlerPlugin,继承该类必须重写 check 和 process 方法 。
check 方法用于检验请求是否符合插件规则 , 以免产生多余请求 。
当check 方法 返回为 true 时会调用 process 方法 。
process 方法里写插件主要检测代码 。
addVulnerable()
当插件检测出漏洞时 , 可以通过调用 addVulnerable() 方法来向数据库插入一条漏洞 。
requests
requests 属性为请求工具包,处理 https 和 http 都很方便 。
response
response 属性为当前爬虫得到的 HTTP 响应 。
task
task 属性为当前任务对象 , 如果你的爬虫插件不是检测漏洞而希望是检测一些敏感信息的话可以修改 task.getResult() 里的属性 。
参考 FingerProbe.java 或 InfoProbe.java 。
target
爬虫爬取到的 URL 对象 。
fetchercrawlerPage
http 请求对象(不建议使用) 。
漏洞扫描插件
漏洞扫描插件会在,扫描任务中勾选“漏洞攻击模块”时调用 。
【go语言模板注入攻击 go语言注解】 漏洞扫描插件分为三种
1.独立插件
独立的漏洞扫描插件需要继承 AbstractExploit 并使用 @Plugin 或 @Exploit
AbstractExploit 中有以下需要了解的方法和属性 。
requests
http / https 发包工具
target 当前扫描任务的地址 。
task
当前扫描任务对象 。
check()
check 是一个抽象方法,需要被子类强制重写 。
该方法一般用于检验是否符合当前漏洞扫描插件的规则,以免产生多与请求 。
attack()
attack 也是一个抽象方法,需要被子类强制重写 。
该方法是检测漏洞的主方法 。
before()
在 attack 方法前执行
after()
在 attack 方法后执行
addVulnerable()
当插件检测出漏洞时,可以通过调用 addVulnerable() 方法来向数据库插入一条漏洞 。
fetchercrawlerPage
http 请求对象(不建议使用) 。
2.漏洞规则
位于
实际上这是一个“内部插件”,会在勾选漏洞模块攻击时调用 。
有一些漏洞检测方法很简单,只通过简单的判断响应体就能识别出来,也就没有必要再去写一个独立的插件而占用空间了 。
在 doSwitch() 方法中会先去根据当前任务的指纹识别结果走一遍 switch 流程 。
swtich 的每一个 case 都是 WEB 指纹的枚举对象 。
当 switch 找到当前任务 WEB 指纹对应的 case 后 , case 内的代码会通过构建一个漏洞规则添加到 loaders 集合里 。
如果规则是通用的,可以写在 switch 的外面 。
3.kunpeng JSON插件
kunpeng 是一个 go 语言编写的 poc 测试框架,这里我对 kunpeng 的 JSON 插件做了一个支持 。
只需要按照 kunpeng json 插件的格式规范创建一个 json 文件到 /resources/json 目录 。
在扫描任务勾选“漏洞攻击模块”时会被调用 , 或通过 MVC 插件调用。
MVC 插件
位于
MVC 插件的特点在于,他可以像是在写一个功能一样,而非简单的接口式调用 。
MVC 插件需要继承 MVCPlugin 类 , 并使用 @Rule,@Plugin 注解 。
MVCPlugin 内置了一个 ModelAndView 对象,是 SpringMVC 提供的 。
可以通过 setViewName() 来指定视图层的网页模板 。
通过 addObject(key,value) 向视图层网页模板注入参数 。
这里的视图层是使用 thymeleaf 实现的 , 需要懂 thymeleaf 的语法 。
例子可以参考:com.trackray.module.inner.JSONPlugin
继承 MVCPlugin 必须要重写一个 index 方法,这是插件的入口 。
如果需要写其他的功能,就得再创建一个 public 返回值为 void 的无参方法 。
并且要在该方法上使用 @Function 注解,该注解的 value 参数如果不填写的话则默认的 requestMapping 地址为方法名 。
例如
最后还需要在 /module/src/main/resources/templates 创建一个目录名为插件 KEY 的目录 。
里面存放扩展名为 .html 的模板文件 。
Python 插件
python 插件有两种实现方式 。
1.通过命令行实现
这种方式最为简单,通过在 include 里写一个 python 脚本 。
然后在插件里调用 shell() 方法来执行系统命令 。
案例可参考 com.trackray.module.plugin.windows.smb.MS17010
但这样还需要再写 java 的代码,对于没有学过 java 的人来说很不友好 。
2.通过jython实现
jython 是一个 Python 语言在 Java 中的完全实现 。
我将它的调用过程写成了一个交互式插件 。
你可以通过在 /resources/python/ 目录下安装如下规范去创建一个 python 文件 。
在这个 python 文件中需要写两个方法 。
关于注解
@Rule
一般用在“可交互插件”和“无交互插件”类上 。
@Plugin
WEB指纹
这里顺便再说一下如何添加指纹库 。
指纹库位于 base 模块,是一个枚举类 。
可以在首部或尾部添加一条新的枚举 , 尽量使用 $ 开头 。
第一个参数是 指纹的名称,如果第二个参数是 String 类型则是该指纹的说明 。
FingerBean 类是指纹匹配对象 。
go模板文件引入js路径问题Go语言模板文件可以引入js文件或css文件,但是在引入的过程中,需要注意以下几点:
1. 引入的文件路径应该是相对路径,而不是绝对路径 。
2. 在引入js文件时,需要使用{{ url }} 模板函数 , 用来拼接路径,这样可以更好的兼容不同的路径 。
3. 如果是在统一的文件夹中的js文件,最好使用{{ static }}模板函数,这样可以更好的节省路径长度 。
4. 在引用js文件时,需要在页面底部,可以使用{{ template }}模板函数,这样可以保证js文件在页面加载完成之前就被加载 。
总之,使用Go语言模板文件引入js文件,需要注意路径的相对性,并且使用模板函数来拼接路径,这样可以更好的兼容不同的路径,从而保证引用js文件的正确性 。
GO语言(十六):模糊测试入门(上)本教程介绍了 Go 中模糊测试的基础知识 。通过模糊测试,随机数据会针对您的测试运行,以尝试找出漏洞或导致崩溃的输入 。可以通过模糊测试发现的一些漏洞示例包括 SQL 注入、缓冲区溢出、拒绝服务和跨站点脚本攻击 。
在本教程中,您将为一个简单的函数编写一个模糊测试,运行 go 命令,并调试和修复代码中的问题 。
首先,为您要编写的代码创建一个文件夹 。
1、打开命令提示符并切换到您的主目录 。
在 Linux 或 Mac 上:
在 Windows 上:
2、在命令提示符下,为您的代码创建一个名为 fuzz 的目录 。
3、创建一个模块来保存您的代码 。
运行go mod init命令,为其提供新代码的模块路径 。
接下来,您将添加一些简单的代码来反转字符串,稍后我们将对其进行模糊测试 。
在此步骤中 , 您将添加一个函数来反转字符串 。
a.使用您的文本编辑器,在 fuzz 目录中创建一个名为 main.go 的文件 。
独立程序(与库相反)始终位于 package 中main 。
此函数将接受string , 使用byte进行循环,并在最后返回反转的字符串 。
此函数将运行一些Reverse操作,然后将输出打印到命令行 。这有助于查看运行中的代码,并可能有助于调试 。
e.该main函数使用 fmt 包,因此您需要导入它 。
第一行代码应如下所示:
从包含 main.go 的目录中的命令行,运行代码 。
可以看到原来的字符串,反转它的结果,然后再反转它的结果,就相当于原来的了 。
现在代码正在运行,是时候测试它了 。
在这一步中 , 您将为Reverse函数编写一个基本的单元测试 。
a.使用您的文本编辑器,在 fuzz 目录中创建一个名为 reverse_test.go 的文件 。
b.将以下代码粘贴到 reverse_test.go 中 。
这个简单的测试将断言列出的输入字符串将被正确反转 。
使用运行单元测试go test
接下来,您将单元测试更改为模糊测试 。
单元测试有局限性 , 即每个输入都必须由开发人员添加到测试中 。模糊测试的一个好处是它可以为您的代码提供输入,并且可以识别您提出的测试用例没有达到的边缘用例 。
在本节中,您将单元测试转换为模糊测试,这样您就可以用更少的工作生成更多的输入!
请注意,您可以将单元测试、基准测试和模糊测试保存在同一个 *_test.go 文件中 , 但对于本示例 , 您将单元测试转换为模糊测试 。
在您的文本编辑器中,将 reverse_test.go 中的单元测试替换为以下模糊测试 。
Fuzzing 也有一些限制 。在您的单元测试中,您可以预测Reverse函数的预期输出 , 并验证实际输出是否满足这些预期 。
例如,在测试用例Reverse("Hello, world")中,单元测试将返回指定为"dlrow ,olleH".
模糊测试时 , 您无法预测预期输出,因为您无法控制输入 。
但是,Reverse您可以在模糊测试中验证函数的一些属性 。在这个模糊测试中检查的两个属性是:
(1)将字符串反转两次保留原始值
(2)反转的字符串将其状态保留为有效的 UTF-8 。
注意单元测试和模糊测试之间的语法差异:
(3)确保新包unicode/utf8已导入 。
随着单元测试转换为模糊测试,是时候再次运行测试了 。
a.在不进行模糊测试的情况下运行模糊测试,以确保种子输入通过 。
如果您在该文件中有其他测试,您也可以运行go test -run=FuzzReverse,并且您只想运行模糊测试 。
b.运行FuzzReverse模糊测试,查看是否有任何随机生成的字符串输入会导致失败 。这是使用go test新标志-fuzz执行的 。
模糊测试时发生故障,导致问题的输入被写入将在下次运行的种子语料库文件中go test,即使没有-fuzz标志也是如此 。要查看导致失败的输入,请在文本编辑器中打开写入 testdata/fuzz/FuzzReverse 目录的语料库文件 。您的种子语料库文件可能包含不同的字符串,但格式相同 。
语料库文件的第一行表示编码版本 。以下每一行代表构成语料库条目的每种类型的值 。由于 fuzz target 只需要 1 个输入,因此版本之后只有 1 个值 。
c.运行没有-fuzz标志的go test; 新的失败种子语料库条目将被使用:
由于我们的测试失败 , 是时候调试了 。
golang反射框架FxFx是一个golang版本的依赖注入框架,它使得golang通过可重用、可组合的模块化来构建golang应用程序变得非常容易,可直接在项目中添加以下内容即可体验Fx效果 。
Fx是通过使用依赖注入的方式替换了全局通过手动方式来连接不同函数调用的复杂度,也不同于其他的依赖注入方式,Fx能够像普通golang函数去使用,而不需要通过使用struct标签或内嵌特定类型 。这样使得Fx能够在很多go的包中很好的使用 。
接下来会提供一些Fx的简单demo,并说明其中的一些定义 。
1、一般步骤
大致的使用步骤就如下 。下面会给出一些完整的demo
2、简单demo
将io.reader与具体实现类关联起来
输出:
3、使用struct参数
前面的使用方式一旦需要进行注入的类型过多 , 可以通过struct参数方式来解决
输出
如果通过Provide提供构造函数是生成相同类型会有什么问题?换句话也就是相同类型拥有多个值呢?
下面两种方式就是来解决这样的问题 。
4、使用struct参数 Name标签
在Fx未使用Name或Group标签时不允许存在多个相同类型的构造函数,一旦存在会触发panic 。
输出
上面通过Name标签即可完成在Fx容器注入相同类型
5、使用struct参数 Group标签
使用group标签同样也能完成上面的功能
输出
基本上Fx简单应用在上面的例子也做了简单讲解
1、Annotated(位于annotated.go文件) 主要用于采用annotated的方式,提供Provide注入类型
源码中Name和Group两个字段与前面提到的Name标签和Group标签是一样的,只能选其一使用
2、App(位于app.go文件) 提供注入对象具体的容器、LiftCycle、容器的启动及停止、类型变量及实现类注入和两者映射等操作
至于Provide和Populate的源码相对比较简单易懂在这里不在描述
具体源码
3、Extract(位于extract.go文件)
主要用于在application启动初始化过程通过依赖注入的方式将容器中的变量值来填充给定的struct , 其中target必须是指向struct的指针,并且只能填充可导出的字段(golang只能通过反射修改可导出并且可寻址的字段),Extract将被Populate代替 。具体源码
4、其他
诸如Populate是用来替换Extract的,而LiftCycle和inout.go涉及内容比较多后续会单独提供专属文件说明 。
在Fx中提供的构造函数都是惰性调用,可以通过invocations在application启动来完成一些必要的初始化工作:fx.Invoke(function); 通过也可以按需自定义实现LiftCycle的Hook对应的OnStart和OnStop用来完成手动启动容器和关闭,来满足一些自己实际的业务需求 。
Fx框架源码解析
主要包括app.go、lifecycle.go、annotated.go、populate.go、inout.go、shutdown.go、extract.go(可以忽略,了解populate.go)以及辅助的internal中的fxlog、fxreflect、lifecycle
go语言模板注入攻击的介绍就聊到这里吧,感谢你花时间阅读本站内容 , 更多关于go语言注解、go语言模板注入攻击的信息别忘了在本站进行查找喔 。

    推荐阅读