@blazedpath/commons 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/blz-base/health/index.js +215 -0
- package/blz-base/index.js +1466 -0
- package/blz-cache/LruCache.js +44 -0
- package/blz-cache/index.js +29 -0
- package/blz-config/index.js +434 -0
- package/blz-core/index.js +364 -0
- package/blz-cryptography/index.js +54 -0
- package/blz-datetimes/index.js +356 -0
- package/blz-file/example.dat +2545 -0
- package/blz-file/fileService.js +205 -0
- package/blz-file/index.js +94 -0
- package/blz-file/index.test.js +31 -0
- package/blz-file/lab.js +33 -0
- package/blz-hazelcast/index.js +189 -0
- package/blz-hazelcast/lib/credentials.js +25 -0
- package/blz-hazelcast/lib/credentialsFactory.js +12 -0
- package/blz-hazelcast/lib/hazelcastCache.js +234 -0
- package/blz-iterable/index.js +446 -0
- package/blz-json-schema/index.js +11 -0
- package/blz-jwt/index.js +121 -0
- package/blz-kafka/index.js +522 -0
- package/blz-math/index.js +131 -0
- package/blz-mongodb/index.js +326 -0
- package/blz-rds/__test__/scape.test.js +58 -0
- package/blz-rds/blz-rds-executor.js +578 -0
- package/blz-rds/blz-rds-helper.js +310 -0
- package/blz-rds/commands/core/add.js +13 -0
- package/blz-rds/commands/core/and.js +18 -0
- package/blz-rds/commands/core/asc.js +10 -0
- package/blz-rds/commands/core/avg.js +10 -0
- package/blz-rds/commands/core/column-ref.js +8 -0
- package/blz-rds/commands/core/count-distinct.js +10 -0
- package/blz-rds/commands/core/count.js +10 -0
- package/blz-rds/commands/core/decimal.js +8 -0
- package/blz-rds/commands/core/desc.js +10 -0
- package/blz-rds/commands/core/distinct.js +10 -0
- package/blz-rds/commands/core/divide.js +11 -0
- package/blz-rds/commands/core/embedded-exists.js +17 -0
- package/blz-rds/commands/core/embedded-select.js +17 -0
- package/blz-rds/commands/core/equals.js +9 -0
- package/blz-rds/commands/core/false.js +8 -0
- package/blz-rds/commands/core/greater-or-equal.js +9 -0
- package/blz-rds/commands/core/greater.js +9 -0
- package/blz-rds/commands/core/in.js +9 -0
- package/blz-rds/commands/core/integer.js +8 -0
- package/blz-rds/commands/core/is-not-null.js +11 -0
- package/blz-rds/commands/core/is-null-or-value.js +10 -0
- package/blz-rds/commands/core/is-null.js +11 -0
- package/blz-rds/commands/core/less-or-equal.js +9 -0
- package/blz-rds/commands/core/less-unary.js +12 -0
- package/blz-rds/commands/core/less.js +9 -0
- package/blz-rds/commands/core/like.js +12 -0
- package/blz-rds/commands/core/max.js +10 -0
- package/blz-rds/commands/core/min.js +10 -0
- package/blz-rds/commands/core/multiply.js +13 -0
- package/blz-rds/commands/core/not-equals.js +9 -0
- package/blz-rds/commands/core/not-in.js +9 -0
- package/blz-rds/commands/core/not.js +13 -0
- package/blz-rds/commands/core/null.js +8 -0
- package/blz-rds/commands/core/nvl.js +11 -0
- package/blz-rds/commands/core/or.js +13 -0
- package/blz-rds/commands/core/parameter.js +34 -0
- package/blz-rds/commands/core/remainder.js +16 -0
- package/blz-rds/commands/core/string.js +8 -0
- package/blz-rds/commands/core/subtract.js +13 -0
- package/blz-rds/commands/core/sum.js +10 -0
- package/blz-rds/commands/core/true.js +8 -0
- package/blz-rds/commands/core/tuple.js +13 -0
- package/blz-rds/commands/datetimes/add-days.js +11 -0
- package/blz-rds/commands/datetimes/add-hours.js +11 -0
- package/blz-rds/commands/datetimes/add-milliseconds.js +11 -0
- package/blz-rds/commands/datetimes/add-minutes.js +11 -0
- package/blz-rds/commands/datetimes/add-months.js +11 -0
- package/blz-rds/commands/datetimes/add-seconds.js +11 -0
- package/blz-rds/commands/datetimes/add-years.js +11 -0
- package/blz-rds/commands/datetimes/date-diff.js +11 -0
- package/blz-rds/commands/datetimes/date.js +12 -0
- package/blz-rds/commands/datetimes/datetime-diff.js +11 -0
- package/blz-rds/commands/datetimes/datetime.js +15 -0
- package/blz-rds/commands/datetimes/day.js +10 -0
- package/blz-rds/commands/datetimes/hour.js +10 -0
- package/blz-rds/commands/datetimes/millisecond.js +10 -0
- package/blz-rds/commands/datetimes/minute.js +10 -0
- package/blz-rds/commands/datetimes/month-text.js +10 -0
- package/blz-rds/commands/datetimes/month.js +10 -0
- package/blz-rds/commands/datetimes/now.js +9 -0
- package/blz-rds/commands/datetimes/second.js +10 -0
- package/blz-rds/commands/datetimes/subtract-days.js +11 -0
- package/blz-rds/commands/datetimes/subtract-hours.js +11 -0
- package/blz-rds/commands/datetimes/subtract-milliseconds.js +11 -0
- package/blz-rds/commands/datetimes/subtract-minutes.js +11 -0
- package/blz-rds/commands/datetimes/subtract-seconds.js +11 -0
- package/blz-rds/commands/datetimes/time-diff.js +11 -0
- package/blz-rds/commands/datetimes/time.js +13 -0
- package/blz-rds/commands/datetimes/today.js +9 -0
- package/blz-rds/commands/datetimes/week-day-text.js +10 -0
- package/blz-rds/commands/datetimes/week-day.js +10 -0
- package/blz-rds/commands/datetimes/week.js +10 -0
- package/blz-rds/commands/datetimes/year.js +10 -0
- package/blz-rds/commands/math/abs.js +10 -0
- package/blz-rds/commands/math/acos.js +10 -0
- package/blz-rds/commands/math/asin.js +10 -0
- package/blz-rds/commands/math/atan.js +10 -0
- package/blz-rds/commands/math/atan2.js +11 -0
- package/blz-rds/commands/math/ceil.js +10 -0
- package/blz-rds/commands/math/cos.js +10 -0
- package/blz-rds/commands/math/cosh.js +10 -0
- package/blz-rds/commands/math/exp.js +10 -0
- package/blz-rds/commands/math/floor.js +10 -0
- package/blz-rds/commands/math/log.js +18 -0
- package/blz-rds/commands/math/log10.js +10 -0
- package/blz-rds/commands/math/pow.js +11 -0
- package/blz-rds/commands/math/random.js +9 -0
- package/blz-rds/commands/math/round.js +18 -0
- package/blz-rds/commands/math/sign.js +10 -0
- package/blz-rds/commands/math/sin.js +10 -0
- package/blz-rds/commands/math/sinh.js +10 -0
- package/blz-rds/commands/math/sqrt.js +10 -0
- package/blz-rds/commands/math/tan.js +10 -0
- package/blz-rds/commands/math/tanh.js +10 -0
- package/blz-rds/commands/math/trunc.js +18 -0
- package/blz-rds/commands/strings/concat.js +20 -0
- package/blz-rds/commands/strings/contains.js +12 -0
- package/blz-rds/commands/strings/ends-with.js +12 -0
- package/blz-rds/commands/strings/index-of.js +11 -0
- package/blz-rds/commands/strings/is-null-or-empty.js +11 -0
- package/blz-rds/commands/strings/is-null-or-white-space.js +11 -0
- package/blz-rds/commands/strings/join.js +22 -0
- package/blz-rds/commands/strings/last-index-of.js +11 -0
- package/blz-rds/commands/strings/length.js +10 -0
- package/blz-rds/commands/strings/pad-left.js +20 -0
- package/blz-rds/commands/strings/pad-right.js +20 -0
- package/blz-rds/commands/strings/replace.js +12 -0
- package/blz-rds/commands/strings/starts-with.js +12 -0
- package/blz-rds/commands/strings/substring.js +12 -0
- package/blz-rds/commands/strings/to-lower.js +10 -0
- package/blz-rds/commands/strings/to-upper.js +10 -0
- package/blz-rds/commands/strings/trim-end.js +10 -0
- package/blz-rds/commands/strings/trim-start.js +10 -0
- package/blz-rds/commands/strings/trim.js +10 -0
- package/blz-rds/index.js +744 -0
- package/blz-rds-mysql/base.js +857 -0
- package/blz-rds-mysql/connection-manager.js +129 -0
- package/blz-rds-mysql/execute-bulk-insert.js +35 -0
- package/blz-rds-mysql/execute-bulk-merge.js +45 -0
- package/blz-rds-mysql/execute-non-query.js +34 -0
- package/blz-rds-mysql/execute-query.js +50 -0
- package/blz-rds-mysql/index.js +41 -0
- package/blz-rds-mysql/stored-procedure.js +207 -0
- package/blz-rds-mysql/syntaxis.json +114 -0
- package/blz-rds-mysqlx/base.js +846 -0
- package/blz-rds-mysqlx/connection-manager.js +141 -0
- package/blz-rds-mysqlx/execute-bulk-insert.js +35 -0
- package/blz-rds-mysqlx/execute-bulk-merge.js +45 -0
- package/blz-rds-mysqlx/execute-non-query.js +29 -0
- package/blz-rds-mysqlx/execute-query.js +39 -0
- package/blz-rds-mysqlx/index.js +41 -0
- package/blz-rds-mysqlx/stored-procedure.js +179 -0
- package/blz-rds-mysqlx/syntaxis.json +105 -0
- package/blz-rds-oracle/index.js +540 -0
- package/blz-rds-oracle/syntaxis.json +112 -0
- package/blz-rds-postgres/base.js +861 -0
- package/blz-rds-postgres/connection-manager.js +225 -0
- package/blz-rds-postgres/execute-bulk-insert.js +81 -0
- package/blz-rds-postgres/execute-bulk-merge.js +93 -0
- package/blz-rds-postgres/execute-non-query.js +23 -0
- package/blz-rds-postgres/execute-query.js +37 -0
- package/blz-rds-postgres/index.js +41 -0
- package/blz-rds-postgres/result-set.js +51 -0
- package/blz-rds-postgres/stored-procedure.js +116 -0
- package/blz-rds-postgres/syntaxis.json +114 -0
- package/blz-redis/index.js +217 -0
- package/blz-redis/lib/redisCache.js +265 -0
- package/blz-regex/index.js +25 -0
- package/blz-security/.eslintrc.js +15 -0
- package/blz-security/__test__/AuthorizationKpn.yaml +1043 -0
- package/blz-security/__test__/FinancingSetting.yaml +177 -0
- package/blz-security/__test__/KpnConfigPortal.yaml +330 -0
- package/blz-security/__test__/OrderManagement.yaml +5190 -0
- package/blz-security/__test__/Security.yaml +128 -0
- package/blz-security/__test__/autorization.test.js +105 -0
- package/blz-security/__test__/orderManagement.test.js +26 -0
- package/blz-security/__test__/secureUrl.test.js +79 -0
- package/blz-security/__test__/solveMergeRule.test.js +109 -0
- package/blz-security/__test__/sqlInjectionGuard.test.js +203 -0
- package/blz-security/__test__/xssGuard.test.js +204 -0
- package/blz-security/authorizationService.js +536 -0
- package/blz-security/config/global.js +8 -0
- package/blz-security/config/welcome +8 -0
- package/blz-security/doc/README.md +75 -0
- package/blz-security/filescanner/index.js +46 -0
- package/blz-security/helpers/consts.js +229 -0
- package/blz-security/helpers/utils.js +267 -0
- package/blz-security/implementations/cache.js +90 -0
- package/blz-security/implementations/oidc.js +404 -0
- package/blz-security/implementations/pkceCacheStore.js +23 -0
- package/blz-security/implementations/saml.js +10 -0
- package/blz-security/implementations/uma.js +63 -0
- package/blz-security/implementations/webAuthn.js +9 -0
- package/blz-security/implementations/wstg.js +72 -0
- package/blz-security/index.js +77 -0
- package/blz-security/lab/index.js +27 -0
- package/blz-security/middleware/HapiServerAzureAd.js +641 -0
- package/blz-security/middleware/HapiServerKeycloak.js +840 -0
- package/blz-security/middleware/HapiServerSimToken.js +247 -0
- package/blz-security/middleware/hapi.js +515 -0
- package/blz-security/middleware/hapiServer.js +974 -0
- package/blz-security/navigationMemoryRepository.js +15 -0
- package/blz-security/navigationMongoDbRepository.js +73 -0
- package/blz-security/secureUrlService.js +47 -0
- package/blz-security/securityService.js +409 -0
- package/blz-security/sqlInjectionGuard.js +162 -0
- package/blz-security/templates/forbidden.html +0 -0
- package/blz-security/templates/session-iframe-azure-ad.html +7 -0
- package/blz-security/templates/session-iframe.html +73 -0
- package/blz-security/templates/unauthorized.html +1 -0
- package/blz-security/xssGuard.js +87 -0
- package/blz-strings/index.js +167 -0
- package/blz-uuid/index.js +7 -0
- package/blz-yaml/index.js +19 -0
- package/index.js +84 -0
- package/package.json +97 -0
- package/process-managers/index.js +422 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
module.exports = class AuthorizationService {
|
|
3
|
+
constructor (utils, logger) {
|
|
4
|
+
this.utils = utils
|
|
5
|
+
this.logger = logger
|
|
6
|
+
this.config = { roles: [], permissions: [] }
|
|
7
|
+
this.WIDGET_SEPARATOR = '|'
|
|
8
|
+
this.WIDGET_SEPARATOR_REPLACE = new RegExp(this.WIDGET_SEPARATOR + '.*$')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
extendConfig (config) {
|
|
12
|
+
// extend permissions
|
|
13
|
+
for (const permission of config.permissions) {
|
|
14
|
+
if (permission.extends && !permission._completed) {
|
|
15
|
+
this.extendPermission(config, permission)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// extend roles
|
|
19
|
+
for (const role of config.roles) {
|
|
20
|
+
if (role.extends && !role._completed) {
|
|
21
|
+
this.extendRole(config, role)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// remove flags
|
|
25
|
+
for (const permission of config.permissions) {
|
|
26
|
+
delete permission._completed
|
|
27
|
+
}
|
|
28
|
+
for (const role of config.roles) {
|
|
29
|
+
delete role._completed
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
extendPermission (config, permission) {
|
|
34
|
+
for (const extend of permission.extends) {
|
|
35
|
+
const base = config.permissions.find(p => p.name === extend)
|
|
36
|
+
if (!base) {
|
|
37
|
+
throw new Error(`Permission ${permission.name} extends ${extend} but not exists`)
|
|
38
|
+
}
|
|
39
|
+
if (base.extends && base.extends.includes(base.name)) {
|
|
40
|
+
throw new Error(`Permission ${permission.name} extends ${base.name} but it is a circular reference`)
|
|
41
|
+
}
|
|
42
|
+
if (base.extends && !base._completed) {
|
|
43
|
+
this.extendPermission(config, base)
|
|
44
|
+
}
|
|
45
|
+
// Agrega las reglas que existen en el permiso base y no en el actual
|
|
46
|
+
for (const rule of base.rules) {
|
|
47
|
+
const exists = permission.rules.find(p => p.path === rule.path && p.actions === rule.actions)
|
|
48
|
+
if (!exists) {
|
|
49
|
+
permission.rules.push(rule)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
permission._completed = true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
extendRole (config, role) {
|
|
57
|
+
for (const extend of role.extends) {
|
|
58
|
+
const base = config.roles.find(p => p.name === extend)
|
|
59
|
+
if (!base) {
|
|
60
|
+
throw new Error(`Role ${role.name} extends ${extend} but not exists`)
|
|
61
|
+
}
|
|
62
|
+
if (base.extends && base.extends.includes(base.name)) {
|
|
63
|
+
throw new Error(`Rome ${role.name} extends ${base.name} but it is a circular reference`)
|
|
64
|
+
}
|
|
65
|
+
if (base.extends && !base._completed) {
|
|
66
|
+
this.extendRole(config, base)
|
|
67
|
+
}
|
|
68
|
+
// Agrega los permisos que existen en el role base y no en el actual
|
|
69
|
+
for (const permission of base.permissions) {
|
|
70
|
+
if (!role.permissions.includes(permission)) {
|
|
71
|
+
role.permissions.push(permission)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
role._completed = true
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
importSecurityConfig (config) {
|
|
80
|
+
this._solveRoleId(config)
|
|
81
|
+
this._validateAndNormalizeConfig(config)
|
|
82
|
+
this.extendConfig(config)
|
|
83
|
+
this._solveRulesByRole(config)
|
|
84
|
+
this._solveMergeRulesByRole(config)
|
|
85
|
+
this.config = config
|
|
86
|
+
let defaultUserRole = process.env.blz_defaultUserRole;
|
|
87
|
+
if (defaultUserRole)
|
|
88
|
+
this.config.defaultUserRole = defaultUserRole;
|
|
89
|
+
return this.config
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getFrontendSecurityRules (roles, domains) {
|
|
93
|
+
const _roles = this._getRoles(roles)
|
|
94
|
+
const frontendRules = []
|
|
95
|
+
for (const role of _roles) {
|
|
96
|
+
const rules = this._getSecurityRulesByRole(role, 'frontend', domains)
|
|
97
|
+
for (const rule of rules) {
|
|
98
|
+
const previousRule = frontendRules.find(p => p.path === rule.path && p.actions === rule.actions)
|
|
99
|
+
if (!previousRule) {
|
|
100
|
+
frontendRules.push(rule)
|
|
101
|
+
} else if (!previousRule.enable && rule.enable) {
|
|
102
|
+
previousRule.enable = rule.enable
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const arr = []
|
|
107
|
+
// Resuelve las reglas por acciones
|
|
108
|
+
for (const rule of frontendRules) {
|
|
109
|
+
if (rule.actions && rule.actions.trim() !== '') {
|
|
110
|
+
const actions = rule.actions.split(',')
|
|
111
|
+
for (const action of actions) {
|
|
112
|
+
arr.push({ path: rule.path.trim() + '|' + action.trim(), enable: rule.enable })
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Resuelve las reglas sin acciones a nivel de path
|
|
117
|
+
for (const rule of frontendRules) {
|
|
118
|
+
if (!rule.actions || rule.actions.trim() === '') {
|
|
119
|
+
const enableRule = arr.some(p => p.path.split('|')[0] === rule.path && p.enable)
|
|
120
|
+
const previousRule = arr.find(p => p.path === rule.path && (!p.actions || p.actions.trim() === ''))
|
|
121
|
+
if (!previousRule) {
|
|
122
|
+
// Si no existe una regla previa, se agrega
|
|
123
|
+
arr.push({ path: rule.path.trim(), enable: enableRule || rule.enable })
|
|
124
|
+
} else if (previousRule && !previousRule.enable && enableRule) {
|
|
125
|
+
// Si existe una regla previa que habilita el path, se habilita
|
|
126
|
+
previousRule.enable = true
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const map = new Map();
|
|
132
|
+
arr.forEach(item => {
|
|
133
|
+
// Si no existe el path en el mapa, lo agregamos.
|
|
134
|
+
if (!map.has(item.path)) {
|
|
135
|
+
map.set(item.path, item);
|
|
136
|
+
} else {
|
|
137
|
+
// Si ya existe, verificamos el flag enable.
|
|
138
|
+
const existing = map.get(item.path);
|
|
139
|
+
// Si el existente es false y el nuevo es true, reemplazamos.
|
|
140
|
+
if (!existing.enable && item.enable) {
|
|
141
|
+
map.set(item.path, item);
|
|
142
|
+
}
|
|
143
|
+
// En caso contrario, dejamos el existente.
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
// Convertimos el mapa a un array.
|
|
147
|
+
const result = Array.from(map.values());
|
|
148
|
+
return result
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getSecurityRules (roles, side, domains) {
|
|
152
|
+
const rules = []
|
|
153
|
+
for (const role of roles) {
|
|
154
|
+
const rulesByRole = this._getSecurityRulesByRole(role, side, domains)
|
|
155
|
+
for (const rule of rulesByRole) {
|
|
156
|
+
if (!rules.map(p => p.path).includes(rule.path)) {
|
|
157
|
+
rules.push(rule)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return rules
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getPermissions () {
|
|
165
|
+
if (!this.config || !this.config.permissions || this.config.permissions.length === 0) {
|
|
166
|
+
return []
|
|
167
|
+
}
|
|
168
|
+
return this.config.permissions.filter(p => p.visible || p.visible === null || p.visible === undefined ).map(p => p.name).sort()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
authorized (path, action, roles) {
|
|
172
|
+
if (this.config.defaultUserRole)
|
|
173
|
+
roles.push(this.config.defaultUserRole);
|
|
174
|
+
if (path.startsWith('/api')) {
|
|
175
|
+
return this._checkApi(path, action, roles)
|
|
176
|
+
} else {
|
|
177
|
+
if (!action || action.trim() === '') {
|
|
178
|
+
return this._checkPath(path, roles)
|
|
179
|
+
} else {
|
|
180
|
+
return this._checkWidget(path, action, roles)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
defaultAuthorized (path, action, roles) {
|
|
186
|
+
const result = this.authorized(path, action, roles)
|
|
187
|
+
if (result === null || result === undefined) {
|
|
188
|
+
return true
|
|
189
|
+
}
|
|
190
|
+
return result
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Private methods //////////////////////////////////////////////////////////////////////////////////////////////
|
|
194
|
+
|
|
195
|
+
_getSecurityRulesByRole (roleId, side, domains) {
|
|
196
|
+
const role = this.config.roles.find(p => p.externalId === roleId)
|
|
197
|
+
if (!role || !role.rules) {
|
|
198
|
+
return []
|
|
199
|
+
}
|
|
200
|
+
const securityRules = role.rules[side]
|
|
201
|
+
if (!securityRules) {
|
|
202
|
+
return []
|
|
203
|
+
}
|
|
204
|
+
if (!domains || domains.length === 0) {
|
|
205
|
+
return securityRules
|
|
206
|
+
}
|
|
207
|
+
return securityRules.filter(p => domains.includes(p.domain))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
_solveRulesByRole (config) {
|
|
212
|
+
for (const role of config.roles) {
|
|
213
|
+
role.rules = { backend: [], frontend: [] }
|
|
214
|
+
for (const permissionName of role.permissions) {
|
|
215
|
+
const permission = config.permissions.find(p => p.name === permissionName)
|
|
216
|
+
if (!permission) {
|
|
217
|
+
continue
|
|
218
|
+
}
|
|
219
|
+
for (const rule of permission.rules) {
|
|
220
|
+
if (rule.path.startsWith('/api')) {
|
|
221
|
+
const exists = role.rules.backend.find(p => p.path === rule.path && p.actions === rule.actions && p.enable === rule.enable)
|
|
222
|
+
if (!exists) {
|
|
223
|
+
rule.domain = permission.domain || 'default'
|
|
224
|
+
role.rules.backend.push(rule)
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
const exists = role.rules.frontend.find(p => p.path === rule.path && p.actions === rule.actions && p.enable === rule.enable)
|
|
228
|
+
if (!exists) {
|
|
229
|
+
rule.domain = permission.domain || 'default'
|
|
230
|
+
role.rules.frontend.push(rule)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
_solveMergeRulesByRole (config) {
|
|
239
|
+
for (const role of config.roles) {
|
|
240
|
+
for (const rule of role.rules.frontend) {
|
|
241
|
+
role.rules.frontend = this._solveMergeRule(role.rules.frontend, rule)
|
|
242
|
+
}
|
|
243
|
+
for (const rule of role.rules.backend) {
|
|
244
|
+
role.rules.backend = this._solveMergeRule(role.rules.backend, rule)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
_solveRoleId (config) {
|
|
250
|
+
for (const role of config.roles) {
|
|
251
|
+
if (!role.externalId) {
|
|
252
|
+
role.externalId = role.name
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
_getRoles (roles) {
|
|
258
|
+
// If you do not have roles assigned, the default roles are assigned.
|
|
259
|
+
const _roles = !roles || roles.length === 0 ? this.config.roles.filter(p => p.default).map(p => p.name) : roles
|
|
260
|
+
// If there are roles that apply to all, they are added
|
|
261
|
+
const applyToAll = this.config.roles.filter(p => p.applyToAll)
|
|
262
|
+
if (applyToAll && applyToAll.length > 0) {
|
|
263
|
+
for (const roleToAdd of applyToAll) {
|
|
264
|
+
if (!_roles.includes(roleToAdd.name)) {
|
|
265
|
+
_roles.push(roleToAdd.name)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return _roles
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_getBackendSecurityRules (roles, action, domains) {
|
|
273
|
+
let rules = []
|
|
274
|
+
const _roles = this._getRoles(roles)
|
|
275
|
+
for (const role of _roles) {
|
|
276
|
+
const securityRules = this._getSecurityRulesByRole(role, 'backend', domains)
|
|
277
|
+
const _rules = securityRules.filter(p => p.actions.includes('*') || p.actions.includes(action))
|
|
278
|
+
for (const rule of _rules) {
|
|
279
|
+
rules = this._solveMergeRule(rules, rule)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return rules.sort((a, b) => (a.path > b.path) ? 1 : -1)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_solveMergeRule (rules, rule) {
|
|
286
|
+
let _rules = JSON.parse(JSON.stringify(rules))
|
|
287
|
+
const previousRules = _rules.filter(p => p.path === rule.path)
|
|
288
|
+
if (!previousRules.length === 0) {
|
|
289
|
+
_rules.push(rule)
|
|
290
|
+
} else {
|
|
291
|
+
if (previousRules.some(p => p.actions.includes('*') && p.enable)) {
|
|
292
|
+
// If a rule already exists that enables all paths
|
|
293
|
+
return _rules
|
|
294
|
+
} else if (rule.actions.includes('*') && rule.enable) {
|
|
295
|
+
// remove rules with the same path and add rule with all actions
|
|
296
|
+
_rules = _rules.filter(p => p.path !== rule.path)
|
|
297
|
+
_rules.push(rule)
|
|
298
|
+
} else if (rule.actions.includes('*') && !rule.enable) {
|
|
299
|
+
// If the rule denies all permissions, it replaces the previous rules that it overrides with this one.
|
|
300
|
+
_rules = _rules.filter(p => !(p.path === rule.path && p.enable === rule.enable))
|
|
301
|
+
_rules.push(rule)
|
|
302
|
+
} else if (!rule.enable && previousRules.some(p => p.actions.includes('*') && !p.enable)) {
|
|
303
|
+
// If the rule disables and a disabling rule already exists, it does not exclude the rule
|
|
304
|
+
return _rules
|
|
305
|
+
} else {
|
|
306
|
+
for (const action of rule.actions.split(',')) {
|
|
307
|
+
const previousRules = _rules.filter(p => p.path === rule.path)
|
|
308
|
+
if (previousRules.some(p => (p.actions.includes('*') || p.actions.includes(action)) && (p.enable || (p.enable === rule.enable)))) {
|
|
309
|
+
// If the action already exists in the previous rule with the same enable state, it is not added
|
|
310
|
+
// If the action exists in the previous rule and is enabled
|
|
311
|
+
continue
|
|
312
|
+
} else if (previousRules.some(p => (p.actions.includes(action) && !p.enable && rule.enable))) {
|
|
313
|
+
// If the action is included but with a disable status and the new rule is enabled, it must be extracted from the previous one and added to the new rule
|
|
314
|
+
const ruleToRemoveAction = previousRules.find(p => (p.actions.includes(action) && !p.enable))
|
|
315
|
+
const newActions = ruleToRemoveAction.actions.split(',').filter(p => p !== action)
|
|
316
|
+
if (newActions.length === 0) {
|
|
317
|
+
// Remove the previous rule
|
|
318
|
+
_rules = _rules.filter(p => !(p.path === rule.path && p.actions === action && !p.enable))
|
|
319
|
+
} else {
|
|
320
|
+
ruleToRemoveAction.actions = newActions.join(',')
|
|
321
|
+
}
|
|
322
|
+
// Search if there is a rule with the same path and enable state
|
|
323
|
+
const previousEnableRule = previousRules.find(p => p.enable === rule.enable)
|
|
324
|
+
if (previousEnableRule) {
|
|
325
|
+
// If it exists, the action is added
|
|
326
|
+
previousEnableRule.actions = previousEnableRule.actions + ',' + action
|
|
327
|
+
} else {
|
|
328
|
+
// If it does not exist, the rule is added
|
|
329
|
+
_rules.push({ path: rule.path, actions: action, enable: rule.enable })
|
|
330
|
+
}
|
|
331
|
+
} else if (previousRules.some(p => (!p.actions.includes(action) && p.enable === rule.enable))) {
|
|
332
|
+
// If the action is not included and the enable state is the same, the action will be added
|
|
333
|
+
const previousRule = previousRules.find(p => p.enable === rule.enable)
|
|
334
|
+
previousRule.actions = previousRule.actions + ',' + action
|
|
335
|
+
} else {
|
|
336
|
+
// Search if there is a rule with the same path and enable state
|
|
337
|
+
const previousEnableRule = previousRules.find(p => p.enable === rule.enable)
|
|
338
|
+
if (previousEnableRule) {
|
|
339
|
+
// If it exists, the action is added
|
|
340
|
+
previousEnableRule.actions = previousEnableRule.actions + ',' + action
|
|
341
|
+
} else {
|
|
342
|
+
// If it does not exist, the rule is added
|
|
343
|
+
_rules.push({ path: rule.path, actions: action, enable: rule.enable })
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return _rules
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_cleanPath (fullUrl) {
|
|
353
|
+
const indexOfQuestionMark = fullUrl.indexOf('?')
|
|
354
|
+
return indexOfQuestionMark !== -1 ? fullUrl.substring(0, indexOfQuestionMark) : fullUrl
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
_cretaeExpression (route) {
|
|
358
|
+
if (this.utils.isRegExp(route)) {
|
|
359
|
+
return route
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Verifica si la ruta termina con "/**"
|
|
363
|
+
if (route.endsWith('/**')) {
|
|
364
|
+
const baseRoute = route
|
|
365
|
+
.replace(/\|.*$/gm, '') // Remueve los parámetros
|
|
366
|
+
.replace(/\/\*\*$/, ''); // Elimina el "/**" del final
|
|
367
|
+
|
|
368
|
+
// Crea la expresión regular que incluye la ruta base y cualquier subruta
|
|
369
|
+
return new RegExp(`^${baseRoute}(\/.*)?$`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const expression = (
|
|
373
|
+
route
|
|
374
|
+
/**
|
|
375
|
+
* Remueve todos los parámetros (si es que existen) que se pasan a la URL:
|
|
376
|
+
* "/customers|add" -> "/customers"
|
|
377
|
+
* "/customers|*" -> "/customers"
|
|
378
|
+
* "/customers|" -> "/customers"
|
|
379
|
+
* "/customers" -> "/customers"
|
|
380
|
+
*/
|
|
381
|
+
.replace(/\|.*$/gm, '')
|
|
382
|
+
/**
|
|
383
|
+
* Remueve todos los "/" que tiene al final la URL
|
|
384
|
+
* "/customers////" -> "/customers"
|
|
385
|
+
* "/customers/" -> "/customers"
|
|
386
|
+
* "/customers" -> "/customers"
|
|
387
|
+
*/
|
|
388
|
+
.replace(/\/+$/, '')
|
|
389
|
+
/**
|
|
390
|
+
* Remueve todos los "/" que tiene al principio la URL y lo lo reemplaza por
|
|
391
|
+
* '^/':
|
|
392
|
+
* "/customers////" -> "/customers"
|
|
393
|
+
* "/customers/" -> "/customers"
|
|
394
|
+
* "/customers" -> "/customers"
|
|
395
|
+
*/
|
|
396
|
+
.replace(/^\/+/, '^/')
|
|
397
|
+
)
|
|
398
|
+
return expression
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
_replaceDynamicURLParts (route) {
|
|
402
|
+
let regexp = null
|
|
403
|
+
if (this.utils.isRegExp(route)) {
|
|
404
|
+
regexp = route
|
|
405
|
+
} else {
|
|
406
|
+
regexp = new RegExp(
|
|
407
|
+
route
|
|
408
|
+
.replace(/\*\*/gm, '.*')
|
|
409
|
+
.replace(/\*/gm, function (match, offset, string) {
|
|
410
|
+
if (string[offset - 1] === '.') {
|
|
411
|
+
return '*'
|
|
412
|
+
}
|
|
413
|
+
return '[^/]*'
|
|
414
|
+
}) + '$',
|
|
415
|
+
''
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
return { regexp }
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
_findMatchedRoutes (url, routes = []) {
|
|
422
|
+
const cleanUrl = url.replace(/^\/+/, '/')
|
|
423
|
+
return routes
|
|
424
|
+
.map((route) => {
|
|
425
|
+
const pathExpression = this._cretaeExpression(route.path)
|
|
426
|
+
const { regexp } = this._replaceDynamicURLParts(pathExpression)
|
|
427
|
+
const match = regexp.test(cleanUrl)
|
|
428
|
+
return match ? route : false
|
|
429
|
+
})
|
|
430
|
+
.filter((route) => route)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
_checkApi (path, action, roles) {
|
|
434
|
+
const cleanedPath = this._cleanPath(path)
|
|
435
|
+
const _action = action.toUpperCase()
|
|
436
|
+
const securityRules = this._getBackendSecurityRules(roles, _action)
|
|
437
|
+
const matchedRoutes = this._findMatchedRoutes(cleanedPath, securityRules)
|
|
438
|
+
const list = this.utils
|
|
439
|
+
.chain(matchedRoutes)
|
|
440
|
+
.filter(p => p.actions.includes('*') || p.actions.includes(_action))
|
|
441
|
+
.value()
|
|
442
|
+
const enabled = list.some(p => p.enable)
|
|
443
|
+
const disabled = list.some(p => !p.enable)
|
|
444
|
+
if (enabled || (!enabled && !disabled)) {
|
|
445
|
+
return true
|
|
446
|
+
}
|
|
447
|
+
if (disabled) {
|
|
448
|
+
this.logger.error(`can't access to ${action} ${path}`)
|
|
449
|
+
return false
|
|
450
|
+
}
|
|
451
|
+
return null
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
_checkPath (path, roles) {
|
|
455
|
+
const securityRules = this.getFrontendSecurityRules(roles)
|
|
456
|
+
const list = this.utils
|
|
457
|
+
.chain(this._findMatchedRoutes(path, securityRules))
|
|
458
|
+
.value()
|
|
459
|
+
const enabled = list.some(p => p.enable)
|
|
460
|
+
const disabled = list.some(p => !p.enable)
|
|
461
|
+
if (enabled || (!enabled && !disabled)) {
|
|
462
|
+
return true
|
|
463
|
+
}
|
|
464
|
+
if (disabled) {
|
|
465
|
+
return false
|
|
466
|
+
}
|
|
467
|
+
return null
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
_checkWidget (path, securityOption, roles) {
|
|
471
|
+
const _securityOption = securityOption.toUpperCase()
|
|
472
|
+
const securityRules = this.getFrontendSecurityRules(roles)
|
|
473
|
+
const rules = this.utils
|
|
474
|
+
.chain(this._findMatchedRoutes(path, securityRules))
|
|
475
|
+
.filter(({ path }) => path.includes(this.WIDGET_SEPARATOR))
|
|
476
|
+
.filter(({ path }) => {
|
|
477
|
+
const expression = path.substring(path.indexOf(this.WIDGET_SEPARATOR) + 1).replace(/\*\*/gm, '.*')
|
|
478
|
+
return new RegExp(`^${expression}$`, 'gm').test(_securityOption)
|
|
479
|
+
})
|
|
480
|
+
.value()
|
|
481
|
+
const allActions = rules.find(p => p.path === path + '|**' && p.enable)
|
|
482
|
+
if (allActions) {
|
|
483
|
+
return true
|
|
484
|
+
}
|
|
485
|
+
const exactlyAction = rules.find(p => p.path === path + '|' + _securityOption)
|
|
486
|
+
if (exactlyAction) {
|
|
487
|
+
return exactlyAction.enable
|
|
488
|
+
}
|
|
489
|
+
const last = this.utils.chain(rules).reverse().first().value()
|
|
490
|
+
if (last) {
|
|
491
|
+
return last.enable
|
|
492
|
+
}
|
|
493
|
+
return null
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
_validateAndNormalizeConfig (config) {
|
|
497
|
+
if (!config) {
|
|
498
|
+
throw new Error('config is undefined')
|
|
499
|
+
}
|
|
500
|
+
if (config.roles === undefined || config.roles === null || config.roles.length === 0) {
|
|
501
|
+
throw new Error('config.roles is undefined')
|
|
502
|
+
}
|
|
503
|
+
if (config.permissions === undefined || config.permissions === null || config.permissions.length === 0) {
|
|
504
|
+
throw new Error('config.permissions is undefined')
|
|
505
|
+
}
|
|
506
|
+
// Validation and normalization of permissions
|
|
507
|
+
for (const permission of config.permissions) {
|
|
508
|
+
if (!permission.name) {
|
|
509
|
+
throw new Error('Permission has not name')
|
|
510
|
+
}
|
|
511
|
+
if (!permission.rules) {
|
|
512
|
+
permission.rules = []
|
|
513
|
+
}
|
|
514
|
+
if (!permission.visible) {
|
|
515
|
+
permission.visible = true
|
|
516
|
+
}
|
|
517
|
+
for (const rule of permission.rules) {
|
|
518
|
+
if (rule.enable === undefined || rule.enable === null) {
|
|
519
|
+
rule.enable = true
|
|
520
|
+
}
|
|
521
|
+
if (rule.actions === undefined || rule.actions === null) {
|
|
522
|
+
rule.actions = rule.path.startsWith('/api') ? '*' : ''
|
|
523
|
+
} else {
|
|
524
|
+
rule.actions = rule.actions.toUpperCase()
|
|
525
|
+
}
|
|
526
|
+
if (!rule.path) {
|
|
527
|
+
throw new Error(`Rule in permission ${permission.name} has not path`)
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
// initialize permissions in roles
|
|
532
|
+
for (const role of config.roles) {
|
|
533
|
+
if (!role.permissions)role.permissions = []
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
██████╗ ██╗ █████╗ ███████╗███████╗██████╗ ██████╗ █████╗ ████████╗██╗ ██╗
|
|
3
|
+
██╔══██╗██║ ██╔══██╗╚══███╔╝██╔════╝██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██║ ██║
|
|
4
|
+
██████╔╝██║ ███████║ ███╔╝ █████╗ ██║ ██║██████╔╝███████║ ██║ ███████║
|
|
5
|
+
██╔══██╗██║ ██╔══██║ ███╔╝ ██╔══╝ ██║ ██║██╔═══╝ ██╔══██║ ██║ ██╔══██║
|
|
6
|
+
██████╔╝███████╗██║ ██║███████╗███████╗██████╔╝██║ ██║ ██║ ██║ ██║ ██║
|
|
7
|
+
╚═════╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
|
8
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Actual solution
|
|
4
|
+
|
|
5
|
+
- Se establece un conjunto de permisos de acuerdo a security rules.
|
|
6
|
+
- Estos security rules permiten habilitar o denegar el acceso vinculado a un path.
|
|
7
|
+
- este path incluye la ruta del web-component y una security option.
|
|
8
|
+
- esa security option puede estar asociado a un widget, una action o una parte de ese widget.
|
|
9
|
+
|
|
10
|
+
### Detected problems
|
|
11
|
+
|
|
12
|
+
- La configuración de seguridad como su resolución están del lado del cliente, por lo cual es vulnerable a ataques.
|
|
13
|
+
- El widget al que se aplica seguridad siempre debe tener definido un path.
|
|
14
|
+
- No se puede definir seguridad en sub-componentes, los cuales no tienen una ruta asociada y pueden ser incluidos en otros componentes.
|
|
15
|
+
- No hay un mecanismo de configuración masivo de seguridad.
|
|
16
|
+
- En IPLAN se estableció una estrategia agregando la configuración en archivos de recursos
|
|
17
|
+
|
|
18
|
+
## Proposal
|
|
19
|
+
|
|
20
|
+
### Requirements
|
|
21
|
+
|
|
22
|
+
- Establecer un conjunto de permisos de acuerdo a un role.
|
|
23
|
+
- Establecer diferentes estrategias de como obtener los roles asociados a un usuario.
|
|
24
|
+
- Una de estas estrategias sera a traves de jwt.
|
|
25
|
+
- Establecer diferentes estrategias de como obtener la configuración de seguridad.
|
|
26
|
+
- A traves de un archivo de recursos (Del lado del servidor)
|
|
27
|
+
- A traves de consumir un servicio rest.
|
|
28
|
+
|
|
29
|
+
- Permitir definir seguridad en sub-componentes.
|
|
30
|
+
- Permitir definir seguridad en componentes que no tienen una ruta asociada.
|
|
31
|
+
|
|
32
|
+
- Establecer funcionalidad de importar y exportar configuración de seguridad
|
|
33
|
+
- Agregar UI para administrar la configuración de seguridad.
|
|
34
|
+
- Lo mas probable es que esta UI este en un paquete aparte.
|
|
35
|
+
|
|
36
|
+
## Se modifica comando para que se pueda consumir desde el backend
|
|
37
|
+
|
|
38
|
+
- Se modifica la definición del comando para que se pueda consumir desde el backend
|
|
39
|
+
- path: ~/blazedapth/blz/blz-suite/frontend/suite/javascripts/descriptors/commands/set-security-rule.js
|
|
40
|
+
- path: ~/blazedapth/blz/blz-suite/frontend/suite/javascripts/descriptors/commands/reset-security-rules.js
|
|
41
|
+
- Se Renombra SecurityClient por BlzSecurity
|
|
42
|
+
- path: ~/blazedapth/blz/blz-builder/burners/web-frontend/sources/startup/index.js
|
|
43
|
+
- path: ~/blazedapth/blz/blz-suite/backend/servers/web-frontend/index.js
|
|
44
|
+
- Se modifica Blz.setSecurityRule por BlzSecurity.setSecurityRule
|
|
45
|
+
- path: ~/blazedapth/blz/blz-shared/js-generator/commands/set-security-rule.js
|
|
46
|
+
- Se modifica Blz.clearSecurityRules por BlzSecurity.clearSecurityRules
|
|
47
|
+
- path: ~/blazedapth/blz/blz-shared/js-generator/commands/reset-security-rules.js
|
|
48
|
+
- Se agrega BlzSecurity en Blz_controls_RouteResolver
|
|
49
|
+
- path: ~/blazedapth/blz/blz-ui/assets/js/blz-wrappers/Blz_controls_RouteResolver.js
|
|
50
|
+
|
|
51
|
+
## Agregar archivo de seguridad para importación masiva
|
|
52
|
+
|
|
53
|
+
## Update blzUi
|
|
54
|
+
|
|
55
|
+
Build Dev:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
cd blz-ui/
|
|
59
|
+
grunt prod-dev
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Build Prod:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
cd blz-ui/
|
|
66
|
+
grunt prod-prod
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## References
|
|
70
|
+
|
|
71
|
+
- Documentation
|
|
72
|
+
- [security rules and options](https://docs-blz.bsn-dev.beesion.team/#/doc/security-rules-and-options)
|
|
73
|
+
- Demo
|
|
74
|
+
- [DemoAuth](~/blazedapth/blz/blz-shared/essentials/solutions/DemoAuth)
|
|
75
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const clamav = require('clamav.js');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scans a readable stream for viruses using ClamAV over TCP.
|
|
6
|
+
*
|
|
7
|
+
* @param {ReadableStream} stream - A Node.js readable stream to scan.
|
|
8
|
+
* @param {Object} [options]
|
|
9
|
+
* @param {number} [options.port=3310] - TCP port where clamd is listening.
|
|
10
|
+
* @param {string} [options.host='127.0.0.1'] - Clamd host.
|
|
11
|
+
* @param {number} [options.timeout=60000] - Timeout in milliseconds.
|
|
12
|
+
* @returns {Promise<{ clean: boolean, name: string }>} - Scan result.
|
|
13
|
+
*/
|
|
14
|
+
async function scanStream(stream, options = {}) {
|
|
15
|
+
const port = options.port || 3310;
|
|
16
|
+
const host = options.host || '127.0.0.1';
|
|
17
|
+
const timeout = options.timeout || 60000;
|
|
18
|
+
|
|
19
|
+
const scanner = clamav.createScanner(port, host, timeout);
|
|
20
|
+
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
scanner.scan(stream, (err, name, isInfected) => {
|
|
23
|
+
if (err) return reject(err);
|
|
24
|
+
resolve({
|
|
25
|
+
clean: !isInfected,
|
|
26
|
+
name,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Checks if a file extension is allowed.
|
|
34
|
+
* @param {string} filename - Name of the file to check.
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
*/
|
|
37
|
+
function fileExtensionAllowed(filename) {
|
|
38
|
+
const ext = path.extname(filename).toLowerCase();
|
|
39
|
+
const allowed = process.env.blz_fileScannerAllowedExtension;
|
|
40
|
+
return allowed ? allowed.split(',').includes(ext) : true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
scanStream,
|
|
45
|
+
fileExtensionAllowed,
|
|
46
|
+
};
|