web_sandbox_console 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +63 -34
- data/app/assets/stylesheets/web_sandbox_console/common.css +14 -1
- data/app/assets/stylesheets/web_sandbox_console/home.css +28 -0
- data/app/controllers/web_sandbox_console/home_controller.rb +2 -5
- data/app/views/layouts/web_sandbox_console/application.html.erb +1 -1
- data/app/views/web_sandbox_console/home/do_view_file.js.erb +6 -8
- data/app/views/web_sandbox_console/home/index.html.erb +3 -26
- data/lib/web_sandbox_console/common.rb +29 -8
- data/lib/web_sandbox_console/safe_ruby.rb +75 -14
- data/lib/web_sandbox_console/sandbox.rb +9 -6
- data/lib/web_sandbox_console/version.rb +1 -1
- data/lib/web_sandbox_console/view_file.rb +45 -7
- metadata +10 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '01385f4ee72595defa82f0d4c7141d28281c09e1b34c946177bda7c592043273'
|
4
|
+
data.tar.gz: 44a4c34af0ed2b38e018491bcb7711d2b20545b7e6a136851bab564efb341025
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9118a01bb98153510454b4626f04c732c19a094db040f451d72e475294f5a99aef7b4590de7d1f7241e3410b853108d9cc79d55bb365e715c197051807681fd
|
7
|
+
data.tar.gz: 93a338b267b25082cbe0815a3ca695df3702197698ad293beb8b016b4769724d36e0cadcaaf47dc561f9dc6203c3dc75bcd9953b10722de443196aecc2dfd700
|
data/README.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
这个控制台提供一个类似沙盒的安全环境,这里做的所有数据操作,都会回滚(不会真正写入数据库);你可以配置 ip 白名单、基本认证,来增加访问安全性;另外在这个沙盒中,内置禁止了linux命令的执行,所以不用担心,ruby越界去做了linux的相关事情,当然还禁止了文件的新建、删除、目录的新建、删除等一系列方法;如果你需要更强的限制,你还可以去配置你想禁止使用的哪些方法等等,具体看配置文件。
|
5
5
|
|
6
|
+
此gem基本涵盖了日常使用的常用功能,包括:数据查询、数据修改、数据导出、日志查看、日志时间或内容过滤、文件查看,日志下载等。
|
7
|
+
|
6
8
|
## Usage
|
7
9
|
使用过程相当简单,和一般的gem,安装后你不用特殊去配置任何东西,就可以正常使用。所有的配置选项都是可选的。
|
8
10
|
|
@@ -19,22 +21,21 @@ $ bundle
|
|
19
21
|
```
|
20
22
|
|
21
23
|
此时,如果是在本地,你访问 `http://localhost:3000/web_sandbox_console` 就能看到web控制台了,下面这个样子。
|
24
|
+
|
22
25
|
PS:代码输入框支持代码高亮,你可以像在代码编辑器一样自由编写代码
|
23
26
|
![Snip20200710_1.png](https://i.loli.net/2020/07/10/XqolSYAIJGKa5xg.png)
|
24
27
|
|
25
28
|
## 配置
|
26
|
-
|
27
|
-
在bundle后,就可以使用一些基础的功能了;
|
28
|
-
如果你不满足基础的功能、或者需要更高的安全性,很有必要仔细了解配置选项,总之还是很推荐对项目进行适当配置;
|
29
|
-
关于配置的详细介绍,在生成的文件中也有详细的说明,参照说明配置即可;
|
30
|
-
```
|
29
|
+
在bundle后,就可以直接使用了,为了安全起见,建议进一步配置基本认证;如果需要数据修改权限,还需要配置公钥;关于配置选项的具体说明,请参阅配置文件注释。
|
31
30
|
|
32
31
|
在 rails 项目路径下,执行:
|
33
32
|
```bash
|
34
33
|
$ rails g web_sandbox_console
|
35
34
|
```
|
36
35
|
|
37
|
-
|
36
|
+
会在项目路径下,创建两个配置文件
|
37
|
+
一、config/initializers/web_sandbox_console.rb文件
|
38
|
+
|
38
39
|
```ruby
|
39
40
|
# config/initializers/web_sandbox_console.rb
|
40
41
|
|
@@ -79,7 +80,9 @@ WebSandboxConsole.setup do |config|
|
|
79
80
|
end
|
80
81
|
```
|
81
82
|
|
82
|
-
|
83
|
+
二、config/web_sandbox_console.yml.example 文件
|
84
|
+
|
85
|
+
主要用于配置基本授权、升级权限需要用到的公钥,参照example文件创建yml文件即可
|
83
86
|
```ruby
|
84
87
|
# config/web_sandbox_console.yml.example
|
85
88
|
|
@@ -91,29 +94,44 @@ web_sandbox_console:
|
|
91
94
|
|
92
95
|
```
|
93
96
|
|
97
|
+
关于配置的补充说明:
|
98
|
+
|
99
|
+
> 建议不要轻易配置黑名单方法,因为禁用某些方法后,可能会导致许多意想不到问题;有可能不小心禁用到rails框架或gem使用的一些方法;
|
100
|
+
>
|
101
|
+
> 对于此gem内置禁用的方法,gem内部是做了一些兼容性的处理的,因此不会有什么问题
|
102
|
+
|
94
103
|
## 深入了解
|
104
|
+
|
95
105
|
主要包含三大功能块:代码执行、文件查看、日志下载,下面分别介绍
|
96
106
|
|
97
107
|
### 代码执行
|
98
108
|
1. 提交和异步执行
|
99
109
|
|
100
|
-
>
|
110
|
+
> 提交后代码会立即执行,及时返回执行结果
|
111
|
+
>
|
112
|
+
> 异步执行,代码会在后台异步执行,这对于执行时间非常长的代码,强烈建议异步执行;比如批量更新数据、导出数据等
|
101
113
|
|
102
114
|
2. 升级权限
|
103
|
-
![Snip20200703_1.png](https://i.loli.net/2020/07/03/26zf5WOBFqmaiHC.png)
|
115
|
+
![Snip20200703_1.png](https://i.loli.net/2020/07/03/26zf5WOBFqmaiHC.png)
|
116
|
+
|
117
|
+
> 大多数时候,可能用到的操作就是查数据,但是,有时你可能需要修改某条数据,那么功能就不够用了
|
118
|
+
>
|
119
|
+
> 为了支持数据的修改,同时保证安全性,加入了升级权限这个功能
|
120
|
+
>
|
121
|
+
> 整个过程相当简单,在yaml文件中配置好公钥 -> 获取token -> 本地加密后回传 -> 授权成功
|
122
|
+
>
|
123
|
+
> 授权成功后,所有数据操作将不再执行回滚(PS:未升级授权时,做的所有数据操作都会执行回滚,不会真正写入数据库)
|
104
124
|
|
105
|
-
```
|
106
|
-
通常你能做的操作就是,查查数据等,数据的所有操作都不会写入到数据库,这样很安全;但是每当你需要修改数据时,还是需要让运维处理;为了满足可以数据写入、和安全性的要求;开发了升级权限这个功能。
|
107
125
|
|
108
|
-
升级权限你需要在配置文件中配置公钥,自己保存私钥;整个过程采用非对称加密的方式,进行授权,还是比较安全的。
|
109
|
-
升级授权成功后,代码执行将不再回滚,会直接写入数据库。
|
110
|
-
```
|
111
126
|
|
112
127
|
升级权限流程如下:
|
113
|
-
>
|
114
|
-
>
|
115
|
-
>
|
116
|
-
>
|
128
|
+
> 1. config/web_sandbox_console.yml`中配置公钥
|
129
|
+
>
|
130
|
+
> 2. 进入授权页面,点击获取令牌
|
131
|
+
>
|
132
|
+
> 3. 用私钥对令牌加密,然后用base64加密
|
133
|
+
>
|
134
|
+
> 4. 将加密的打印结果(注意是puts 文本),输入加密密文框,提交
|
117
135
|
|
118
136
|
```ruby
|
119
137
|
# 本地生成 加密密文代码
|
@@ -131,35 +149,46 @@ puts encode_text
|
|
131
149
|
![Snip20200703_2.png](https://i.loli.net/2020/07/03/zFMjpRSX8fCDQ2i.png)
|
132
150
|
1. 目录和文件
|
133
151
|
|
134
|
-
> 你可以查看一个目录下有哪些文件夹或文件,你也可以直接查看文件的内容,默认返回一个文件的前100行
|
152
|
+
> 你可以查看一个目录下有哪些文件夹或文件,你也可以直接查看文件的内容,默认返回一个文件的前100行
|
135
153
|
|
136
154
|
2. 指定行数
|
137
|
-
```
|
138
|
-
你可以根据文件总行数,指定查看文件的开始行数、结束行数。
|
139
|
-
PS: a. 在过滤文件(过滤内容/过滤时间)的时候,此时指定行数将被忽略
|
140
|
-
b. 对于大文件(默认超过10M),处于性能考虑,此时指定行数也会被忽略
|
141
|
-
```
|
142
155
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
156
|
+
> 你可以根据文件总行数,指定查看文件的开始行数、结束行数。
|
157
|
+
> PS: a. 在过滤文件(过滤内容/过滤时间)的时候,此时指定行数将被忽略
|
158
|
+
> b. 对于大文件(默认超过10M),处于性能考虑,此时指定行数也会被忽略
|
159
|
+
3. 过滤内容
|
160
|
+
|
161
|
+
> 当过滤内容时,仅返回匹配的行,匹配行的上下文是不会返回的
|
162
|
+
>
|
163
|
+
> 因此如果需要查看匹配行的上下文,需要再次根据匹配行的时间做过滤(PS:此时需清掉内容输入框)
|
148
164
|
|
165
|
+
4. 过滤时间
|
166
|
+
|
167
|
+
> 可以只填开始时间,不填结束时间,此时返回该时间的日志
|
149
168
|
4. 权限
|
150
169
|
|
151
|
-
> 默认情况,只能查看日志文件或目录,当然你也可以去配置做调整。
|
170
|
+
> 默认情况,只能查看日志文件或目录,当然你也可以去配置做调整。
|
171
|
+
|
172
|
+
5. 其它
|
173
|
+
|
174
|
+
> 1. 可以根据返回内容的提示,了解当前查询(文件总行数、当前按照某种方式返回)
|
175
|
+
> 2. 查询的逻辑如下:
|
176
|
+
> 3. 如果只指定文件名,且文件比较小(小文件),默认返回文件前100行
|
177
|
+
> 4. 如果只指定文件名,文件比较大(大文件),默认返回文件最后1000行
|
178
|
+
> 5. 如果指定了文件名、过滤内容,则忽略行数查找,直接依据过滤内容匹配行,如果匹配到的行数超过1000行,则返回匹配出的前1000行
|
179
|
+
> 6. 如果指定了文件名、过滤内容、过滤时间,则先按照时间匹配出内容,然后根据内容进行匹配
|
152
180
|
|
153
181
|
### 日志下载
|
182
|
+
|
154
183
|
![Snip20200703_3.png](https://i.loli.net/2020/07/03/csW5OfEhPVeSbJz.png)
|
155
184
|
你可以直接输入文件名(需要带后缀)下载日志
|
156
185
|
|
157
186
|
### 关于数据导出
|
158
|
-
|
187
|
+
|
188
|
+
可以先在代码执行页面,用`CSV.open`方式生成 csv文件,然后在日志下载中去下载创建的csv文件
|
159
189
|
|
160
190
|
有一下几点需注意:
|
161
|
-
> 1.
|
162
|
-
> 2. 文件名以你路径中(/分隔)的最后一个名称为准
|
191
|
+
> 1. 在`CSV.open`中写的任何路径,都不会生效,最终只会在log目录下创建csv文件;比如`CSV.open('#{Rails.root}/hu/bar.txt') 会在log目录下,生成bar.csv文件
|
163
192
|
> 3. csv 文件下载后,会自动删除掉,因此只能下载一次
|
164
193
|
|
165
194
|
|
@@ -167,4 +196,4 @@ PS: a. 在过滤文件(过滤内容/过滤时间)的时候,此时指定行
|
|
167
196
|
Contribution directions go here.
|
168
197
|
|
169
198
|
## License
|
170
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
199
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -186,6 +186,8 @@ img {
|
|
186
186
|
position: relative;
|
187
187
|
margin-left: 200px;
|
188
188
|
/*padding-top: 70px;*/
|
189
|
+
margin-right: 30px;
|
190
|
+
min-height: 800px;
|
189
191
|
z-index: 100
|
190
192
|
}
|
191
193
|
|
@@ -215,4 +217,15 @@ img {
|
|
215
217
|
|
216
218
|
.mr-auto {
|
217
219
|
margin-right: auto!important;
|
218
|
-
}
|
220
|
+
}
|
221
|
+
|
222
|
+
|
223
|
+
pre {
|
224
|
+
white-space: pre-wrap; /* Since CSS 2.1 */
|
225
|
+
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
226
|
+
white-space: -pre-wrap; /* Opera 4-6 */
|
227
|
+
white-space: -o-pre-wrap; /* Opera 7 */
|
228
|
+
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
229
|
+
line-height: 18px;
|
230
|
+
margin-top: 0px;
|
231
|
+
}
|
@@ -20,4 +20,32 @@
|
|
20
20
|
font-size: 15px;
|
21
21
|
color: white;
|
22
22
|
cursor: pointer;
|
23
|
+
}
|
24
|
+
|
25
|
+
/* 执行代码页面样式 */
|
26
|
+
.wrapper-console {
|
27
|
+
position: relative;
|
28
|
+
top: 8px;
|
29
|
+
width: 100%;
|
30
|
+
height: 300px;
|
31
|
+
}
|
32
|
+
|
33
|
+
.left {
|
34
|
+
height: 100%;
|
35
|
+
/* 为了不占用绝对定位的宽度 这里计算下宽度*/
|
36
|
+
width: calc(100% - 200px);
|
37
|
+
}
|
38
|
+
|
39
|
+
.right {
|
40
|
+
position: absolute;
|
41
|
+
width: 180px;
|
42
|
+
top: 0;
|
43
|
+
bottom: 0;
|
44
|
+
right: 0;
|
45
|
+
padding-left: 15px;
|
46
|
+
}
|
47
|
+
|
48
|
+
/* 选中后code背景色*/
|
49
|
+
.CodeMirror-selected {
|
50
|
+
background: #20aee3 !important;
|
23
51
|
}
|
@@ -22,11 +22,8 @@ module WebSandboxConsole
|
|
22
22
|
|
23
23
|
# 查看文件
|
24
24
|
def do_view_file
|
25
|
-
|
26
|
-
@
|
27
|
-
@total_line_num = results[:total_line_num]
|
28
|
-
@touch_grep_protect = results[:touch_grep_protect]
|
29
|
-
@content_is_trimed = results[:content_is_trimed]
|
25
|
+
@result_hash = ViewFile.new(params).view
|
26
|
+
@tip_hash = WebSandboxConsole::ViewFile::PageTips
|
30
27
|
end
|
31
28
|
|
32
29
|
# 下载文件页面
|
@@ -1,16 +1,14 @@
|
|
1
1
|
$(".output-content").empty();
|
2
|
-
<% line_num_show = @total_line_num ? "原始文件总行数:#{@total_line_num}" : "" %>
|
3
2
|
|
3
|
+
<% line_num_show = @result_hash[:total_line_num] ? "原始文件总行数:#{@result_hash[:total_line_num]}" : "" %>
|
4
4
|
$(".output-content").append("<p>============ <%= Time.current %> 查找结果如下 <%= line_num_show %> ===============</p>");
|
5
5
|
|
6
|
-
<%
|
7
|
-
|
6
|
+
<% @tip_hash.each do |key,value| %>
|
7
|
+
<% if @result_hash[key.to_sym] %>
|
8
|
+
$(".output-content").append("<p>PS: <%= value %></p>");
|
9
|
+
<% end %>
|
8
10
|
<% end %>
|
9
11
|
|
10
|
-
<%
|
11
|
-
$(".output-content").append("<p>PS: 当前返回内容太多,仅展示满足条件的1000行</p>");
|
12
|
-
<% end %>
|
13
|
-
|
14
|
-
<% @lines.each do |result| %>
|
12
|
+
<% @result_hash[:lines].each do |result| %>
|
15
13
|
$(".output-content").append('<pre><%= escape_javascript result %></pre>');
|
16
14
|
<% end %>
|
@@ -1,30 +1,5 @@
|
|
1
1
|
<h3>执行代码</h3>
|
2
2
|
|
3
|
-
<style type="text/css">
|
4
|
-
.wrapper-console {
|
5
|
-
position: relative;
|
6
|
-
top: 8px;
|
7
|
-
width: 100%;
|
8
|
-
height: 300px;
|
9
|
-
}
|
10
|
-
|
11
|
-
.left {
|
12
|
-
height: 100%;
|
13
|
-
/* 为了不占用绝对定位的宽度 这里计算下宽度*/
|
14
|
-
width: calc(100% - 200px);
|
15
|
-
}
|
16
|
-
|
17
|
-
.right {
|
18
|
-
position: absolute;
|
19
|
-
width: 180px;
|
20
|
-
top: 0;
|
21
|
-
bottom: 0;
|
22
|
-
right: 0;
|
23
|
-
padding-left: 15px;
|
24
|
-
}
|
25
|
-
</style>
|
26
|
-
|
27
|
-
|
28
3
|
<div class="wrapper-console">
|
29
4
|
<%= form_tag(web_sandbox_console.eval_code_path, remote: true, class: 'form') do %>
|
30
5
|
<div class= "left">
|
@@ -60,6 +35,8 @@
|
|
60
35
|
indentWithTabs: true,
|
61
36
|
theme: 'lucario',
|
62
37
|
tabSize: 2,
|
63
|
-
lineNumbers: true
|
38
|
+
lineNumbers: true,
|
39
|
+
lineWrapping: true, /* 自动换行 */
|
40
|
+
fixedGutter: false
|
64
41
|
});
|
65
42
|
</script>
|
@@ -1,17 +1,38 @@
|
|
1
1
|
module WebSandboxConsole
|
2
2
|
module Common
|
3
|
-
|
4
|
-
def
|
5
|
-
@
|
3
|
+
|
4
|
+
def current_uuid(uuid=nil)
|
5
|
+
@uuid ||= uuid
|
6
|
+
end
|
7
|
+
|
8
|
+
# logger sql语句
|
9
|
+
def logger_sql
|
10
|
+
logger = fetch_logger
|
11
|
+
logger.level = 0
|
12
|
+
logger.formatter = proc {|severity, time, progname, msg| "#{current_uuid}: #{msg}\n"}
|
13
|
+
ActiveRecord::Base.logger = logger
|
14
|
+
end
|
6
15
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
16
|
+
# uuid 方便取出日志
|
17
|
+
def log_p(msg_or_exce, is_general_text = false)
|
18
|
+
uuid = current_uuid
|
19
|
+
logger = fetch_logger
|
20
|
+
|
21
|
+
if msg_or_exce.respond_to?(:message) # 异常
|
22
|
+
logger.info "#{uuid}:" + msg_or_exce.message
|
23
|
+
logger.info "#{uuid}:" + msg_or_exce.backtrace.join("|||")
|
24
|
+
elsif is_general_text # 普通文本
|
25
|
+
logger.info "#{uuid}:" + msg_or_exce.inspect
|
26
|
+
else # 返回值
|
27
|
+
logger.info "#{uuid}: => " + msg_or_exce.inspect
|
12
28
|
end
|
13
29
|
end
|
14
30
|
|
31
|
+
# 获取 logger
|
32
|
+
def fetch_logger
|
33
|
+
@logger ||= Logger.new(log_path, 'daily')
|
34
|
+
end
|
35
|
+
|
15
36
|
def log_path
|
16
37
|
"#{Rails.root}/#{self.console_log_path || "log/web_sandbox_console.log"}"
|
17
38
|
end
|
@@ -7,17 +7,14 @@ module WebSandboxConsole
|
|
7
7
|
sanitize_class_methods
|
8
8
|
sanitize_logger_new
|
9
9
|
sanitize_csv
|
10
|
+
compatible_file_cache
|
11
|
+
compatible_i18n_translate
|
12
|
+
blacklist_method_remind
|
10
13
|
end
|
11
14
|
|
12
15
|
# 净化 类方法
|
13
16
|
def sanitize_class_methods
|
14
|
-
|
15
|
-
merge_method_hash(CLASS_METHOD_BUILT_IN_BLACKLIST, class_method_blacklist)
|
16
|
-
else
|
17
|
-
CLASS_METHOD_BUILT_IN_BLACKLIST
|
18
|
-
end
|
19
|
-
|
20
|
-
blacklist.each do |klass, methods|
|
17
|
+
class_method_blacklists.each do |klass, methods|
|
21
18
|
klass = Object.const_get(klass)
|
22
19
|
methods.each do |method|
|
23
20
|
next if klass.singleton_methods.exclude?(method)
|
@@ -28,13 +25,7 @@ module WebSandboxConsole
|
|
28
25
|
|
29
26
|
# 净化 实例方法
|
30
27
|
def sanitize_instance_methods
|
31
|
-
|
32
|
-
merge_method_hash(INSTANT_METOD_BUILT_IN_BLACKLIST,instance_method_blacklist)
|
33
|
-
else
|
34
|
-
INSTANT_METOD_BUILT_IN_BLACKLIST
|
35
|
-
end
|
36
|
-
|
37
|
-
blacklist.each do |klass, methods|
|
28
|
+
instance_method_blacklists.each do |klass, methods|
|
38
29
|
klass = Object.const_get(klass)
|
39
30
|
methods.each do |method|
|
40
31
|
next if (klass != Kernel) && klass.instance_methods.exclude?(method)
|
@@ -43,6 +34,24 @@ module WebSandboxConsole
|
|
43
34
|
end
|
44
35
|
end
|
45
36
|
|
37
|
+
# 类方法黑名单列表
|
38
|
+
def class_method_blacklists
|
39
|
+
blacklist = if class_method_blacklist
|
40
|
+
merge_method_hash(CLASS_METHOD_BUILT_IN_BLACKLIST, class_method_blacklist)
|
41
|
+
else
|
42
|
+
CLASS_METHOD_BUILT_IN_BLACKLIST
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# 实例方法黑名单列表
|
47
|
+
def instance_method_blacklists
|
48
|
+
blacklist = if instance_method_blacklist
|
49
|
+
merge_method_hash(INSTANT_METOD_BUILT_IN_BLACKLIST,instance_method_blacklist)
|
50
|
+
else
|
51
|
+
INSTANT_METOD_BUILT_IN_BLACKLIST
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
46
55
|
# 净化 常量
|
47
56
|
def sanitize_constants
|
48
57
|
return unless constant_blacklist
|
@@ -105,5 +114,57 @@ module WebSandboxConsole
|
|
105
114
|
end
|
106
115
|
end
|
107
116
|
|
117
|
+
# 兼容文件缓存
|
118
|
+
def compatible_file_cache
|
119
|
+
ActiveSupport::Cache::FileStore.class_exec do
|
120
|
+
def write_entry(key, entry, options)
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
124
|
+
def delete_entry(key, options)
|
125
|
+
true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# 兼容翻译
|
131
|
+
def compatible_i18n_translate
|
132
|
+
I18n.instance_exec do
|
133
|
+
def translate(*args)
|
134
|
+
"ActiveRecord::RecordInvalid: 校验失败"
|
135
|
+
end
|
136
|
+
alias :t :translate
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# 当拦截黑名单方法时提醒
|
141
|
+
def blacklist_method_remind
|
142
|
+
Kernel.class_exec do
|
143
|
+
# 发现此处method_missing Array 没有flatten方法
|
144
|
+
def flatten_arr(arr)
|
145
|
+
new_arr = []
|
146
|
+
arr.each do |e|
|
147
|
+
if e.is_a?(Array)
|
148
|
+
new_arr.concat(flatten_arr(e))
|
149
|
+
else
|
150
|
+
new_arr << e
|
151
|
+
end
|
152
|
+
end
|
153
|
+
new_arr
|
154
|
+
end
|
155
|
+
|
156
|
+
def method_missing(name,*params)
|
157
|
+
class_methods = WebSandboxConsole.class_method_blacklists.values
|
158
|
+
instance_methods = WebSandboxConsole.instance_method_blacklists.values
|
159
|
+
|
160
|
+
if flatten_arr([class_methods, instance_methods]).include?(name.to_sym)
|
161
|
+
msg = "PS:当前代码执行过程中可能调用了黑名单方法,若代码正常返回,请忽略此条提醒"
|
162
|
+
WebSandboxConsole.log_p(msg, true)
|
163
|
+
end
|
164
|
+
super
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
108
169
|
end
|
109
170
|
end
|
@@ -43,16 +43,18 @@ module WebSandboxConsole
|
|
43
43
|
|
44
44
|
def runner_code
|
45
45
|
str =<<-CODE
|
46
|
-
WebSandboxConsole.init_safe_env
|
47
46
|
result = nil
|
48
47
|
begin
|
48
|
+
WebSandboxConsole.current_uuid("#{self.uuid}")
|
49
|
+
WebSandboxConsole.init_safe_env
|
50
|
+
WebSandboxConsole.logger_sql
|
49
51
|
#{self.pass_auth ? no_rollback_code : rollback_code}
|
50
52
|
rescue Exception => e
|
51
|
-
WebSandboxConsole.log_p(e
|
53
|
+
WebSandboxConsole.log_p(e)
|
52
54
|
rescue SyntaxError => e
|
53
|
-
WebSandboxConsole.log_p(e
|
55
|
+
WebSandboxConsole.log_p(e)
|
54
56
|
end
|
55
|
-
WebSandboxConsole.log_p(result
|
57
|
+
WebSandboxConsole.log_p(result)
|
56
58
|
CODE
|
57
59
|
end
|
58
60
|
|
@@ -121,7 +123,7 @@ module WebSandboxConsole
|
|
121
123
|
|
122
124
|
# 返回结果
|
123
125
|
def return_result_arr
|
124
|
-
last_10_lines = `tail -n
|
126
|
+
last_10_lines = `tail -n 100 #{WebSandboxConsole.log_path} | grep #{self.uuid}`
|
125
127
|
|
126
128
|
last_10_lines.split("\n").map do |line|
|
127
129
|
line.split("#{self.uuid}:").last.split("|||")
|
@@ -131,7 +133,8 @@ module WebSandboxConsole
|
|
131
133
|
# 最终结果
|
132
134
|
def get_result
|
133
135
|
if @stdout.present?
|
134
|
-
stdout_arr =
|
136
|
+
stdout_arr = ['------------ 打印值 ----------']
|
137
|
+
stdout_arr.concat(@stdout.to_s.split("\n"))
|
135
138
|
stdout_arr << '------------ 返回值 ----------'
|
136
139
|
stdout_arr.concat(return_result_arr)
|
137
140
|
else
|
@@ -8,21 +8,32 @@ module WebSandboxConsole
|
|
8
8
|
attr_accessor :grep_content # 过滤内容
|
9
9
|
attr_accessor :touch_grep_protect # 是否触发过滤保护
|
10
10
|
attr_accessor :content_is_trimed # 内容是否有裁剪
|
11
|
+
|
12
|
+
# 页面相关提示
|
13
|
+
PageTips = {
|
14
|
+
touch_grep_protect: "由于过滤执行时间太长,已触发过滤保护,返回内容为最后1000行",
|
15
|
+
content_is_trimed: "当前返回内容太多,仅展示满足条件的1000行",
|
16
|
+
is_big_file_return: "当前文件视为大文件,返回最后1000行",
|
17
|
+
special_line_return: "当前按指定行数返回,若未指定行数,则默认返回文件前100行"
|
18
|
+
}.freeze
|
11
19
|
|
12
20
|
def initialize(opts = {})
|
13
21
|
@file_or_dir = opts[:file_or_dir]
|
14
22
|
@start_line_num = (opts[:start_line_num].presence || 1).to_i
|
15
23
|
@end_line_num = (opts[:end_line_num].presence || 100).to_i
|
16
|
-
@sed_start_time =
|
17
|
-
@sed_end_time =
|
24
|
+
@sed_start_time = opts[:sed_start_time]
|
25
|
+
@sed_end_time = opts[:sed_end_time]
|
18
26
|
@grep_content = opts[:grep_content]
|
19
|
-
@touch_grep_protect
|
20
|
-
@content_is_trimed
|
27
|
+
@touch_grep_protect = false
|
28
|
+
@content_is_trimed = false
|
29
|
+
@is_big_file_return = false
|
30
|
+
@special_line_return = false
|
21
31
|
end
|
22
32
|
|
23
33
|
def view
|
24
34
|
begin
|
25
35
|
check_param
|
36
|
+
correction_line_num
|
26
37
|
file_or_dir_exists
|
27
38
|
check_blacklist
|
28
39
|
check_only_view_log
|
@@ -38,6 +49,13 @@ module WebSandboxConsole
|
|
38
49
|
raise ViewFileError, "过滤内容中不能出现单引号" if grep_content.to_s.include?("'")
|
39
50
|
end
|
40
51
|
|
52
|
+
# 纠正起始行数
|
53
|
+
def correction_line_num
|
54
|
+
if @start_line_num > @end_line_num
|
55
|
+
@end_line_num = @start_line_num + 100
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
41
59
|
# 转换成项目路径
|
42
60
|
def project_path(path)
|
43
61
|
"#{Rails.root}/#{path}"
|
@@ -117,11 +135,14 @@ module WebSandboxConsole
|
|
117
135
|
if is_directory?(file_or_dir_path)
|
118
136
|
files_in_dir
|
119
137
|
else # 文件
|
138
|
+
parse_start_and_end_time
|
120
139
|
lines = if need_grep?
|
121
140
|
grep_file_content
|
122
141
|
elsif is_big_file?
|
142
|
+
@is_big_file_return = true
|
123
143
|
tail_any_line(1000)
|
124
144
|
else
|
145
|
+
@special_line_return = true
|
125
146
|
special_line_content
|
126
147
|
end
|
127
148
|
# 修剪行数
|
@@ -131,7 +152,9 @@ module WebSandboxConsole
|
|
131
152
|
lines: add_line_num(lines),
|
132
153
|
total_line_num: cal_file_total_line_num,
|
133
154
|
touch_grep_protect: @touch_grep_protect,
|
134
|
-
content_is_trimed: @content_is_trimed
|
155
|
+
content_is_trimed: @content_is_trimed,
|
156
|
+
is_big_file_return: @is_big_file_return,
|
157
|
+
special_line_return: @special_line_return
|
135
158
|
}
|
136
159
|
end
|
137
160
|
end
|
@@ -210,8 +233,23 @@ module WebSandboxConsole
|
|
210
233
|
|
211
234
|
private
|
212
235
|
# 解析时间
|
213
|
-
def
|
214
|
-
|
236
|
+
def parse_start_and_end_time
|
237
|
+
formatter = datetime_formatter
|
238
|
+
@sed_start_time = DateTime.parse(@sed_start_time).strftime(formatter) rescue nil
|
239
|
+
@sed_end_time = DateTime.parse(@sed_end_time).strftime(formatter) rescue nil
|
240
|
+
end
|
241
|
+
|
242
|
+
# 日志时间格式是否为标准格式
|
243
|
+
def logger_datetime_is_default_formatter
|
244
|
+
# 抽取日志的第二行
|
245
|
+
logger_line = `head -n 2 #{file_or_dir_path} | tail -n 1`
|
246
|
+
datatime_part = logger_line.split(/DEBUG:|INFO:|WARN:|ERROR:|FATAL:|UNKNOWN:/).first.to_s
|
247
|
+
datatime_part.include?("T")
|
248
|
+
end
|
249
|
+
|
250
|
+
# 解析日期格式
|
251
|
+
def datetime_formatter
|
252
|
+
logger_datetime_is_default_formatter ? "%FT%T" : "%F %T"
|
215
253
|
end
|
216
254
|
|
217
255
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: web_sandbox_console
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- dongmingyan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -38,8 +38,13 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description:
|
42
|
-
|
41
|
+
description: At work, many times, we need to connect to the server to enter the rails
|
42
|
+
c query or some data. When the operator has plenty of time, the situation is relatively
|
43
|
+
good; if an operator is responsible for many servers at the same time, it will waste
|
44
|
+
a lot of time to help with the query; to solve this problem, I want to find a secure
|
45
|
+
and convenient query console, after searching some gem, found and did not meet my
|
46
|
+
expectations gem, so decided to write a related function of the console designed
|
47
|
+
to provide a safe, convenient web.
|
43
48
|
email:
|
44
49
|
- dongmingyan01@gmail.com
|
45
50
|
executables: []
|
@@ -115,5 +120,5 @@ requirements: []
|
|
115
120
|
rubygems_version: 3.0.8
|
116
121
|
signing_key:
|
117
122
|
specification_version: 4
|
118
|
-
summary:
|
123
|
+
summary: A secure, convenient web console
|
119
124
|
test_files: []
|