# 开发插件

本节介绍如何开发并发布一个 JavaScript 检测插件。

#### 开发环境准备 <a href="#prepare" id="prepare"></a>

1. 下载并安装 [NodeJS](https://nodejs.org/en/download/)，Ubuntu 系统可使用 `sudo apt-get install -y nodejs` 命令进行安装
2. 安装插件开发工具 openraspjs:

```bash
npm install -g openrasp
```

#### 编写第一个插件 <a href="#start" id="start"></a>

检测插件针对应用的行为进行检测，当应用执行数据库查询、文件读写等操作，OpenRASP 引擎就会调用检测插件，并将相关参数一并传递过来。一个最小的SQL检测插件如下所示:

```js
const plugin_version = '2018-1000-1000'
const plugin_name    = 'test-plugin'

'use strict'
var plugin  = new RASP(plugin_name)

const clean = {
    action:     'ignore',
    message:    'Looks fine to me',
    confidence: 0
}

// BEGIN ALGORITHM CONFIG //

var algorithmConfig = {}

// END ALGORITHM CONFIG //

plugin.register('sql', function (params, context) {
    plugin.log('SQL query: ' + params.query)
    return clean
})

plugin.log('plugin-demo: plugin loaded')
```

**首先**，我们调用 `plugin.register` 注册了SQL查询的检测函数，并将SQL语句打印到插件日志。其中，

* `params` 为检查点提供的参数，如SQL语句、要读取的文件等等
* `context` 为请求信息，如请求参数，服务器信息等等

**然后**，我们在回调函数里，调用 `plugin.log` 打印了SQL查询语句

**最后**，我们在回调函数里，返回检测结果，即 "放行"

目前我们支持14个检测点，具体请看 [参数说明](https://github.com/baidu-security/openrasp-docs-old/blob/main/dev/data.html) 文档。

#### 调用插件接口 <a href="#api" id="api"></a>

我们在 RASP 类里提供了一些接口，可以直接在插件里调用。比如，你可以调用 RASP.sql\_tokenize 将SQL语句转化为 token:

```js
plugin.register('sql', function (params, context) {
    plugin.log('SQL tokens ', RASP.sql_tokenize(params.query, params.server))
    return clean
})
```

详细 JS API 说明请看 [接口说明](https://github.com/baidu-security/openrasp-docs-old/blob/main/dev/api.html) 文档。

#### 测试检测插件 <a href="#test" id="test"></a>

现在，我们已经完成了检测插件，下一步是进行测试。测试有两种方法:

1. 参考 [单元测试](https://github.com/baidu-security/openrasp-docs-old/blob/main/dev/test/main.html)，编写JSON测试用例并使用 openraspjs 进行测试
2. 参考 [安装插件](https://github.com/baidu-security/openrasp-docs-old/blob/main/setup/standalone.html#plugin)，将插件放到安装了 OpenRASP 的应用服务器进行测试

若你是初次接触插件开发，我们推荐使用第二种方式，即使用 OpenRASP 单机版调试。具体调试方法请参考上述文档，这里不再赘述。

#### 拦截危险操作 <a href="#action" id="action"></a>

现在，我们已经能够编写检测插件，并在客户端运行。接下来就是拦截攻击了，在回调函数里，将返回的 action 字段改为 `block` 即可拦截请求。比如，当SQL语句包含union注入特征，我们想拦截整个请求的话，可以这样写:

```js
plugin.register('sql', function (params, context) {
    plugin.log('SQL tokens ', RASP.sql_tokenize(params.query, params.server))
    
    if (/union.*select.*from.*information_schema/.test(params.query)) {
        return {
            action:     'block',
            message:    '拦截SQL查询，因为XXX',
            confidence: 90
        }
    }
    
    return clean
})
```

在官方插件里，实际的检测算法要复杂的很多，有兴趣可以参考 [official.js](https://github.com/baidu/openrasp/tree/master/plugins/official/plugin.js) 的实现。如果不想拦截攻击，只是想记录攻击日志，可将 action 字段设置为 `log`。

最后， `confidence` 字段用来标记报警的可信度，官方插件的范围是 `90~100`，越高报警的可靠性越高。

**注意**: 当某个插件拦截了攻击，其他插件将不在被调用。

#### FAQ <a href="#faq" id="faq"></a>

**1. 插件支持 ES6 语法吗？{#faq-1}**

* 插件环境可能不支持某些最新的 JavaScript 语法，请使用 openrasjs 进行语法检查
* 插件不能使用与平台相关的全局对象，只能使用 [JavaScript 标准内建对象](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)
* 插件可以像编写 Node.js 程序一样引入其他模块，然后通过 webpack 或 browserify 等工具打包成一个文件，但是注意不能引入与平台相关的模块，例如: Node.js 的 http 模块

**2. 如果检测点没有返回对象，或者返回的对象不合法，会怎么样？{#faq-2}**

* 若检查点没有返回对象，等于 `ignore`
* 若检查点返回了对象，但 `action` 值不存在或者不为 `block/log/ignore` 之一，等价于 `log`
