slicejs-web-framework 2.3.4 → 2.4.0
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/LICENSE +21 -21
- package/README.md +24 -5
- package/Slice/Components/Structural/Controller/Controller.js +156 -154
- package/Slice/Components/Structural/Debugger/Debugger.css +619 -619
- package/Slice/Components/Structural/Debugger/Debugger.html +72 -72
- package/Slice/Components/Structural/Logger/Log.js +10 -10
- package/Slice/Components/Structural/StylesManager/StylesManager.js +6 -3
- package/Slice/Slice.js +111 -51
- package/api/index.js +261 -239
- package/api/middleware/securityMiddleware.js +252 -252
- package/package.json +37 -37
- package/sliceConfig.schema.json +4 -0
- package/src/App/index.html +22 -22
- package/src/App/index.js +23 -23
- package/src/App/style.css +40 -40
- package/src/Components/AppComponents/HomePage/HomePage.css +204 -204
- package/src/Components/AppComponents/HomePage/HomePage.html +48 -48
- package/src/Components/AppComponents/HomePage/HomePage.js +195 -195
- package/src/Components/AppComponents/Playground/Playground.css +11 -11
- package/src/Components/AppComponents/Playground/Playground.js +111 -111
- package/src/Components/Service/FetchManager/FetchManager.js +133 -133
- package/src/Components/Service/IndexedDbManager/IndexedDbManager.js +141 -141
- package/src/Components/Service/LocalStorageManager/LocalStorageManager.js +45 -45
- package/src/Components/Visual/Button/Button.css +47 -47
- package/src/Components/Visual/Button/Button.html +5 -5
- package/src/Components/Visual/Button/Button.js +92 -92
- package/src/Components/Visual/Card/Card.css +68 -68
- package/src/Components/Visual/Card/Card.html +7 -7
- package/src/Components/Visual/Card/Card.js +107 -107
- package/src/Components/Visual/Checkbox/Checkbox.css +87 -87
- package/src/Components/Visual/Checkbox/Checkbox.html +8 -8
- package/src/Components/Visual/Checkbox/Checkbox.js +86 -86
- package/src/Components/Visual/CodeVisualizer/CodeVisualizer.css +129 -129
- package/src/Components/Visual/CodeVisualizer/CodeVisualizer.html +3 -3
- package/src/Components/Visual/CodeVisualizer/CodeVisualizer.js +259 -259
- package/src/Components/Visual/Details/Details.css +70 -70
- package/src/Components/Visual/Details/Details.html +9 -9
- package/src/Components/Visual/Details/Details.js +76 -76
- package/src/Components/Visual/DropDown/DropDown.css +60 -60
- package/src/Components/Visual/DropDown/DropDown.html +5 -5
- package/src/Components/Visual/DropDown/DropDown.js +63 -63
- package/src/Components/Visual/Grid/Grid.css +7 -7
- package/src/Components/Visual/Grid/Grid.html +1 -1
- package/src/Components/Visual/Grid/Grid.js +57 -57
- package/src/Components/Visual/Icon/Icon.css +510 -510
- package/src/Components/Visual/Icon/Icon.js +89 -89
- package/src/Components/Visual/Icon/slc.json +554 -554
- package/src/Components/Visual/Icon/slc.styl +507 -507
- package/src/Components/Visual/Icon/slc.svg +1485 -1485
- package/src/Components/Visual/Icon/slc.symbol.svg +1058 -1058
- package/src/Components/Visual/Input/Input.css +91 -91
- package/src/Components/Visual/Input/Input.html +4 -4
- package/src/Components/Visual/Input/Input.js +215 -215
- package/src/Components/Visual/Layout/Layout.js +49 -49
- package/src/Components/Visual/Link/Link.css +8 -0
- package/src/Components/Visual/Link/Link.html +1 -0
- package/src/Components/Visual/Link/Link.js +63 -0
- package/src/Components/Visual/Loading/Loading.css +56 -56
- package/src/Components/Visual/Loading/Loading.html +83 -83
- package/src/Components/Visual/Loading/Loading.js +38 -38
- package/src/Components/Visual/MultiRoute/MultiRoute.js +93 -93
- package/src/Components/Visual/Navbar/Navbar.css +115 -115
- package/src/Components/Visual/Navbar/Navbar.html +44 -44
- package/src/Components/Visual/Navbar/Navbar.js +141 -141
- package/src/Components/Visual/NotFound/NotFound.css +116 -116
- package/src/Components/Visual/NotFound/NotFound.html +23 -23
- package/src/Components/Visual/NotFound/NotFound.js +16 -16
- package/src/Components/Visual/Route/Route.js +93 -93
- package/src/Components/Visual/Select/Select.css +84 -84
- package/src/Components/Visual/Select/Select.html +8 -8
- package/src/Components/Visual/Select/Select.js +195 -195
- package/src/Components/Visual/Switch/Switch.css +76 -76
- package/src/Components/Visual/Switch/Switch.html +8 -8
- package/src/Components/Visual/Switch/Switch.js +102 -102
- package/src/Components/Visual/TreeItem/TreeItem.css +36 -36
- package/src/Components/Visual/TreeItem/TreeItem.html +1 -1
- package/src/Components/Visual/TreeItem/TreeItem.js +126 -126
- package/src/Components/Visual/TreeView/TreeView.css +8 -8
- package/src/Components/Visual/TreeView/TreeView.html +1 -1
- package/src/Components/Visual/TreeView/TreeView.js +48 -48
- package/src/Components/components.js +2 -2
- package/src/Styles/sliceStyles.css +34 -34
- package/src/Themes/Dark.css +42 -42
- package/src/Themes/Light.css +31 -31
- package/src/Themes/Slice.css +47 -47
- package/src/routes.js +15 -15
- package/src/sliceConfig.json +73 -60
- package/src/testing.js +887 -887
- package/src/Components/Service/Link/Link.js +0 -26
|
@@ -1,253 +1,253 @@
|
|
|
1
|
-
// api/middleware/securityMiddleware.js
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Middleware de seguridad para prevenir acceso directo malicioso
|
|
6
|
-
* pero permitir que la aplicación cargue sus dependencias normalmente
|
|
7
|
-
*/
|
|
8
|
-
export function securityMiddleware(options = {}) {
|
|
9
|
-
const {
|
|
10
|
-
allowedExtensions = ['.js', '.css', '.html', '.json', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.woff', '.woff2', '.ttf'],
|
|
11
|
-
blockedPaths = [
|
|
12
|
-
'/node_modules',
|
|
13
|
-
'/package.json',
|
|
14
|
-
'/package-lock.json',
|
|
15
|
-
'/.env',
|
|
16
|
-
'/.git'
|
|
17
|
-
],
|
|
18
|
-
allowPublicAssets = true
|
|
19
|
-
} = options;
|
|
20
|
-
|
|
21
|
-
return (req, res, next) => {
|
|
22
|
-
const requestPath = req.path;
|
|
23
|
-
|
|
24
|
-
// 1. Bloquear acceso a rutas definitivamente sensibles (configuración, dependencias)
|
|
25
|
-
const isBlockedPath = blockedPaths.some(blocked =>
|
|
26
|
-
requestPath.startsWith(blocked) || requestPath.includes(blocked)
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
if (isBlockedPath) {
|
|
30
|
-
console.warn(`🚫 Blocked access to sensitive path: ${requestPath}`);
|
|
31
|
-
return res.status(403).json({
|
|
32
|
-
error: 'Forbidden',
|
|
33
|
-
message: 'Access to this resource is not allowed',
|
|
34
|
-
path: requestPath
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 2. Permitir acceso a assets públicos
|
|
39
|
-
if (allowPublicAssets) {
|
|
40
|
-
const publicPaths = ['/assets', '/public', '/images', '/styles'];
|
|
41
|
-
const isPublicAsset = publicPaths.some(publicPath =>
|
|
42
|
-
requestPath.startsWith(publicPath)
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
if (isPublicAsset) {
|
|
46
|
-
return next();
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 3. Validar extensiones de archivo
|
|
51
|
-
const fileExtension = path.extname(requestPath).toLowerCase();
|
|
52
|
-
|
|
53
|
-
if (fileExtension && !allowedExtensions.includes(fileExtension)) {
|
|
54
|
-
console.warn(`🚫 Blocked file type: ${requestPath}`);
|
|
55
|
-
return res.status(403).json({
|
|
56
|
-
error: 'Forbidden',
|
|
57
|
-
message: 'File type not allowed',
|
|
58
|
-
extension: fileExtension
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 4. Prevenir path traversal attacks
|
|
63
|
-
const normalizedPath = path.normalize(requestPath);
|
|
64
|
-
if (normalizedPath.includes('..') || normalizedPath.includes('~')) {
|
|
65
|
-
console.warn(`🚫 Path traversal attempt: ${requestPath}`);
|
|
66
|
-
return res.status(403).json({
|
|
67
|
-
error: 'Forbidden',
|
|
68
|
-
message: 'Invalid path',
|
|
69
|
-
path: requestPath
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Todo está bien, continuar
|
|
74
|
-
next();
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Middleware específico para proteger archivos del framework Slice.js
|
|
80
|
-
* PERMITE acceso cuando viene desde la propia aplicación (Referer válido)
|
|
81
|
-
* BLOQUEA acceso directo desde navegador o herramientas externas
|
|
82
|
-
*/
|
|
83
|
-
export function sliceFrameworkProtection(options = {}) {
|
|
84
|
-
const {
|
|
85
|
-
port = 3000,
|
|
86
|
-
strictMode = false,
|
|
87
|
-
allowedDomains = [] // Dominios personalizados permitidos
|
|
88
|
-
} = options;
|
|
89
|
-
|
|
90
|
-
return (req, res, next) => {
|
|
91
|
-
const requestPath = req.path;
|
|
92
|
-
|
|
93
|
-
// Rutas del framework que requieren verificación
|
|
94
|
-
const frameworkPaths = [
|
|
95
|
-
'/Slice/Components/Structural',
|
|
96
|
-
'/Slice/Core',
|
|
97
|
-
'/Slice/Services'
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
const isFrameworkFile = frameworkPaths.some(fwPath =>
|
|
101
|
-
requestPath.startsWith(fwPath)
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
if (!isFrameworkFile) {
|
|
105
|
-
return next();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Verificar el origen de la petición
|
|
109
|
-
const referer = req.get('Referer') || req.get('Referrer');
|
|
110
|
-
const origin = req.get('Origin');
|
|
111
|
-
const host = req.get('Host');
|
|
112
|
-
|
|
113
|
-
// Construir lista de orígenes válidos dinámicamente
|
|
114
|
-
const validOrigins = [
|
|
115
|
-
`http://localhost:${port}`,
|
|
116
|
-
`http://127.0.0.1:${port}`,
|
|
117
|
-
`http://0.0.0.0:${port}`,
|
|
118
|
-
`https://localhost:${port}`,
|
|
119
|
-
...allowedDomains // Dominios personalizados del usuario
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
// Si hay un Host header, agregarlo automáticamente
|
|
123
|
-
if (host) {
|
|
124
|
-
validOrigins.push(`http://${host}`);
|
|
125
|
-
validOrigins.push(`https://${host}`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Verificar si la petición viene de un origen válido
|
|
129
|
-
const hasValidReferer = referer && validOrigins.some(valid => referer.startsWith(valid));
|
|
130
|
-
const hasValidOrigin = origin && validOrigins.some(valid => origin === valid);
|
|
131
|
-
const isSameHost = host && referer && referer.includes(host);
|
|
132
|
-
|
|
133
|
-
// Permitir si viene desde la aplicación
|
|
134
|
-
if (hasValidReferer || hasValidOrigin || isSameHost) {
|
|
135
|
-
return next();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// En modo estricto, bloquear todo acceso sin referer válido
|
|
139
|
-
if (strictMode) {
|
|
140
|
-
console.warn(`🚫 Blocked direct framework file access: ${requestPath}`);
|
|
141
|
-
return res.status(403).json({
|
|
142
|
-
error: 'Framework Protection',
|
|
143
|
-
message: 'Direct access to Slice.js framework files is blocked',
|
|
144
|
-
tip: 'Framework files must be loaded through the application',
|
|
145
|
-
path: requestPath
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// En modo normal (desarrollo), permitir pero advertir
|
|
150
|
-
console.warn(`⚠️ Framework file accessed without valid referer: ${requestPath}`);
|
|
151
|
-
next();
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Middleware para logging de peticiones sospechosas
|
|
157
|
-
*/
|
|
158
|
-
export function suspiciousRequestLogger() {
|
|
159
|
-
const suspiciousPatterns = [
|
|
160
|
-
/\.\.\//, // Path traversal
|
|
161
|
-
/~/, // Home directory access
|
|
162
|
-
/\.env/, // Environment files
|
|
163
|
-
/\.git/, // Git files
|
|
164
|
-
/package\.json/, // Package files
|
|
165
|
-
/package-lock\.json/,
|
|
166
|
-
/node_modules/, // Dependencies
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
return (req, res, next) => {
|
|
170
|
-
const requestPath = req.path;
|
|
171
|
-
|
|
172
|
-
const isSuspicious = suspiciousPatterns.some(pattern =>
|
|
173
|
-
pattern.test(requestPath)
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
if (isSuspicious) {
|
|
177
|
-
const clientIp = req.ip || req.connection.remoteAddress;
|
|
178
|
-
console.warn(`⚠️ Suspicious request: ${requestPath} from ${clientIp}`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
next();
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Middleware para bloquear acceso directo vía navegador (typing en la URL)
|
|
187
|
-
* pero permitir peticiones desde scripts (fetch, import, etc.)
|
|
188
|
-
*/
|
|
189
|
-
export function directAccessProtection(options = {}) {
|
|
190
|
-
const { protectedPaths = [] } = options;
|
|
191
|
-
|
|
192
|
-
return (req, res, next) => {
|
|
193
|
-
const requestPath = req.path;
|
|
194
|
-
|
|
195
|
-
const isProtectedPath = protectedPaths.some(protectedPath =>
|
|
196
|
-
requestPath.startsWith(protectedPath)
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
if (!isProtectedPath) {
|
|
200
|
-
return next();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Detectar acceso directo:
|
|
204
|
-
// - No tiene Referer (usuario escribió la URL directamente)
|
|
205
|
-
// - Accept header indica navegación HTML
|
|
206
|
-
const referer = req.get('Referer');
|
|
207
|
-
const accept = req.get('Accept') || '';
|
|
208
|
-
|
|
209
|
-
const isDirectBrowserAccess = !referer && accept.includes('text/html');
|
|
210
|
-
|
|
211
|
-
if (isDirectBrowserAccess) {
|
|
212
|
-
console.warn(`🚫 Blocked direct browser access: ${requestPath}`);
|
|
213
|
-
return res.status(403).send(`
|
|
214
|
-
<!DOCTYPE html>
|
|
215
|
-
<html>
|
|
216
|
-
<head>
|
|
217
|
-
<title>Access Denied</title>
|
|
218
|
-
<style>
|
|
219
|
-
body {
|
|
220
|
-
font-family: system-ui;
|
|
221
|
-
max-width: 600px;
|
|
222
|
-
margin: 100px auto;
|
|
223
|
-
padding: 20px;
|
|
224
|
-
}
|
|
225
|
-
h1 { color: #d32f2f; }
|
|
226
|
-
code {
|
|
227
|
-
background: #f5f5f5;
|
|
228
|
-
padding: 2px 6px;
|
|
229
|
-
border-radius: 3px;
|
|
230
|
-
}
|
|
231
|
-
</style>
|
|
232
|
-
</head>
|
|
233
|
-
<body>
|
|
234
|
-
<h1>🚫 Direct Access Denied</h1>
|
|
235
|
-
<p>This file cannot be accessed directly.</p>
|
|
236
|
-
<p>Path: <code>${requestPath}</code></p>
|
|
237
|
-
<p>Framework files are automatically loaded by the application.</p>
|
|
238
|
-
<p><a href="/">← Return to application</a></p>
|
|
239
|
-
</body>
|
|
240
|
-
</html>
|
|
241
|
-
`);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
next();
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export default {
|
|
249
|
-
securityMiddleware,
|
|
250
|
-
sliceFrameworkProtection,
|
|
251
|
-
suspiciousRequestLogger,
|
|
252
|
-
directAccessProtection
|
|
1
|
+
// api/middleware/securityMiddleware.js
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Middleware de seguridad para prevenir acceso directo malicioso
|
|
6
|
+
* pero permitir que la aplicación cargue sus dependencias normalmente
|
|
7
|
+
*/
|
|
8
|
+
export function securityMiddleware(options = {}) {
|
|
9
|
+
const {
|
|
10
|
+
allowedExtensions = ['.js', '.css', '.html', '.json', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.woff', '.woff2', '.ttf'],
|
|
11
|
+
blockedPaths = [
|
|
12
|
+
'/node_modules',
|
|
13
|
+
'/package.json',
|
|
14
|
+
'/package-lock.json',
|
|
15
|
+
'/.env',
|
|
16
|
+
'/.git'
|
|
17
|
+
],
|
|
18
|
+
allowPublicAssets = true
|
|
19
|
+
} = options;
|
|
20
|
+
|
|
21
|
+
return (req, res, next) => {
|
|
22
|
+
const requestPath = req.path;
|
|
23
|
+
|
|
24
|
+
// 1. Bloquear acceso a rutas definitivamente sensibles (configuración, dependencias)
|
|
25
|
+
const isBlockedPath = blockedPaths.some(blocked =>
|
|
26
|
+
requestPath.startsWith(blocked) || requestPath.includes(blocked)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (isBlockedPath) {
|
|
30
|
+
console.warn(`🚫 Blocked access to sensitive path: ${requestPath}`);
|
|
31
|
+
return res.status(403).json({
|
|
32
|
+
error: 'Forbidden',
|
|
33
|
+
message: 'Access to this resource is not allowed',
|
|
34
|
+
path: requestPath
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2. Permitir acceso a assets públicos
|
|
39
|
+
if (allowPublicAssets) {
|
|
40
|
+
const publicPaths = ['/assets', '/public', '/images', '/styles'];
|
|
41
|
+
const isPublicAsset = publicPaths.some(publicPath =>
|
|
42
|
+
requestPath.startsWith(publicPath)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (isPublicAsset) {
|
|
46
|
+
return next();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 3. Validar extensiones de archivo
|
|
51
|
+
const fileExtension = path.extname(requestPath).toLowerCase();
|
|
52
|
+
|
|
53
|
+
if (fileExtension && !allowedExtensions.includes(fileExtension)) {
|
|
54
|
+
console.warn(`🚫 Blocked file type: ${requestPath}`);
|
|
55
|
+
return res.status(403).json({
|
|
56
|
+
error: 'Forbidden',
|
|
57
|
+
message: 'File type not allowed',
|
|
58
|
+
extension: fileExtension
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 4. Prevenir path traversal attacks
|
|
63
|
+
const normalizedPath = path.normalize(requestPath);
|
|
64
|
+
if (normalizedPath.includes('..') || normalizedPath.includes('~')) {
|
|
65
|
+
console.warn(`🚫 Path traversal attempt: ${requestPath}`);
|
|
66
|
+
return res.status(403).json({
|
|
67
|
+
error: 'Forbidden',
|
|
68
|
+
message: 'Invalid path',
|
|
69
|
+
path: requestPath
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Todo está bien, continuar
|
|
74
|
+
next();
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Middleware específico para proteger archivos del framework Slice.js
|
|
80
|
+
* PERMITE acceso cuando viene desde la propia aplicación (Referer válido)
|
|
81
|
+
* BLOQUEA acceso directo desde navegador o herramientas externas
|
|
82
|
+
*/
|
|
83
|
+
export function sliceFrameworkProtection(options = {}) {
|
|
84
|
+
const {
|
|
85
|
+
port = 3000,
|
|
86
|
+
strictMode = false,
|
|
87
|
+
allowedDomains = [] // Dominios personalizados permitidos
|
|
88
|
+
} = options;
|
|
89
|
+
|
|
90
|
+
return (req, res, next) => {
|
|
91
|
+
const requestPath = req.path;
|
|
92
|
+
|
|
93
|
+
// Rutas del framework que requieren verificación
|
|
94
|
+
const frameworkPaths = [
|
|
95
|
+
'/Slice/Components/Structural',
|
|
96
|
+
'/Slice/Core',
|
|
97
|
+
'/Slice/Services'
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const isFrameworkFile = frameworkPaths.some(fwPath =>
|
|
101
|
+
requestPath.startsWith(fwPath)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (!isFrameworkFile) {
|
|
105
|
+
return next();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Verificar el origen de la petición
|
|
109
|
+
const referer = req.get('Referer') || req.get('Referrer');
|
|
110
|
+
const origin = req.get('Origin');
|
|
111
|
+
const host = req.get('Host');
|
|
112
|
+
|
|
113
|
+
// Construir lista de orígenes válidos dinámicamente
|
|
114
|
+
const validOrigins = [
|
|
115
|
+
`http://localhost:${port}`,
|
|
116
|
+
`http://127.0.0.1:${port}`,
|
|
117
|
+
`http://0.0.0.0:${port}`,
|
|
118
|
+
`https://localhost:${port}`,
|
|
119
|
+
...allowedDomains // Dominios personalizados del usuario
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
// Si hay un Host header, agregarlo automáticamente
|
|
123
|
+
if (host) {
|
|
124
|
+
validOrigins.push(`http://${host}`);
|
|
125
|
+
validOrigins.push(`https://${host}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Verificar si la petición viene de un origen válido
|
|
129
|
+
const hasValidReferer = referer && validOrigins.some(valid => referer.startsWith(valid));
|
|
130
|
+
const hasValidOrigin = origin && validOrigins.some(valid => origin === valid);
|
|
131
|
+
const isSameHost = host && referer && referer.includes(host);
|
|
132
|
+
|
|
133
|
+
// Permitir si viene desde la aplicación
|
|
134
|
+
if (hasValidReferer || hasValidOrigin || isSameHost) {
|
|
135
|
+
return next();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// En modo estricto, bloquear todo acceso sin referer válido
|
|
139
|
+
if (strictMode) {
|
|
140
|
+
console.warn(`🚫 Blocked direct framework file access: ${requestPath}`);
|
|
141
|
+
return res.status(403).json({
|
|
142
|
+
error: 'Framework Protection',
|
|
143
|
+
message: 'Direct access to Slice.js framework files is blocked',
|
|
144
|
+
tip: 'Framework files must be loaded through the application',
|
|
145
|
+
path: requestPath
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// En modo normal (desarrollo), permitir pero advertir
|
|
150
|
+
console.warn(`⚠️ Framework file accessed without valid referer: ${requestPath}`);
|
|
151
|
+
next();
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Middleware para logging de peticiones sospechosas
|
|
157
|
+
*/
|
|
158
|
+
export function suspiciousRequestLogger() {
|
|
159
|
+
const suspiciousPatterns = [
|
|
160
|
+
/\.\.\//, // Path traversal
|
|
161
|
+
/~/, // Home directory access
|
|
162
|
+
/\.env/, // Environment files
|
|
163
|
+
/\.git/, // Git files
|
|
164
|
+
/package\.json/, // Package files
|
|
165
|
+
/package-lock\.json/,
|
|
166
|
+
/node_modules/, // Dependencies
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
return (req, res, next) => {
|
|
170
|
+
const requestPath = req.path;
|
|
171
|
+
|
|
172
|
+
const isSuspicious = suspiciousPatterns.some(pattern =>
|
|
173
|
+
pattern.test(requestPath)
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
if (isSuspicious) {
|
|
177
|
+
const clientIp = req.ip || req.connection.remoteAddress;
|
|
178
|
+
console.warn(`⚠️ Suspicious request: ${requestPath} from ${clientIp}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
next();
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Middleware para bloquear acceso directo vía navegador (typing en la URL)
|
|
187
|
+
* pero permitir peticiones desde scripts (fetch, import, etc.)
|
|
188
|
+
*/
|
|
189
|
+
export function directAccessProtection(options = {}) {
|
|
190
|
+
const { protectedPaths = [] } = options;
|
|
191
|
+
|
|
192
|
+
return (req, res, next) => {
|
|
193
|
+
const requestPath = req.path;
|
|
194
|
+
|
|
195
|
+
const isProtectedPath = protectedPaths.some(protectedPath =>
|
|
196
|
+
requestPath.startsWith(protectedPath)
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (!isProtectedPath) {
|
|
200
|
+
return next();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Detectar acceso directo:
|
|
204
|
+
// - No tiene Referer (usuario escribió la URL directamente)
|
|
205
|
+
// - Accept header indica navegación HTML
|
|
206
|
+
const referer = req.get('Referer');
|
|
207
|
+
const accept = req.get('Accept') || '';
|
|
208
|
+
|
|
209
|
+
const isDirectBrowserAccess = !referer && accept.includes('text/html');
|
|
210
|
+
|
|
211
|
+
if (isDirectBrowserAccess) {
|
|
212
|
+
console.warn(`🚫 Blocked direct browser access: ${requestPath}`);
|
|
213
|
+
return res.status(403).send(`
|
|
214
|
+
<!DOCTYPE html>
|
|
215
|
+
<html>
|
|
216
|
+
<head>
|
|
217
|
+
<title>Access Denied</title>
|
|
218
|
+
<style>
|
|
219
|
+
body {
|
|
220
|
+
font-family: system-ui;
|
|
221
|
+
max-width: 600px;
|
|
222
|
+
margin: 100px auto;
|
|
223
|
+
padding: 20px;
|
|
224
|
+
}
|
|
225
|
+
h1 { color: #d32f2f; }
|
|
226
|
+
code {
|
|
227
|
+
background: #f5f5f5;
|
|
228
|
+
padding: 2px 6px;
|
|
229
|
+
border-radius: 3px;
|
|
230
|
+
}
|
|
231
|
+
</style>
|
|
232
|
+
</head>
|
|
233
|
+
<body>
|
|
234
|
+
<h1>🚫 Direct Access Denied</h1>
|
|
235
|
+
<p>This file cannot be accessed directly.</p>
|
|
236
|
+
<p>Path: <code>${requestPath}</code></p>
|
|
237
|
+
<p>Framework files are automatically loaded by the application.</p>
|
|
238
|
+
<p><a href="/">← Return to application</a></p>
|
|
239
|
+
</body>
|
|
240
|
+
</html>
|
|
241
|
+
`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
next();
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export default {
|
|
249
|
+
securityMiddleware,
|
|
250
|
+
sliceFrameworkProtection,
|
|
251
|
+
suspiciousRequestLogger,
|
|
252
|
+
directAccessProtection
|
|
253
253
|
};
|
package/package.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "slicejs-web-framework",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "",
|
|
5
|
-
"engines": {
|
|
6
|
-
"node": ">=20"
|
|
7
|
-
},
|
|
8
|
-
"scripts": {
|
|
9
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
10
|
-
"slice:init": "node node_modules/slicejs-cli/client.js init",
|
|
11
|
-
"slice:create": "node node_modules/slicejs-cli/client.js component create",
|
|
12
|
-
"slice:delete": "node node_modules/slicejs-cli/client.js component delete",
|
|
13
|
-
"slice:list": "node node_modules/slicejs-cli/client.js component list",
|
|
14
|
-
"slice:start": "node api/index.js",
|
|
15
|
-
"format": "prettier --write \"**/*.js\"",
|
|
16
|
-
"run": "node api/index.js",
|
|
17
|
-
"development": "npm run",
|
|
18
|
-
"slice:modify": "node node_modules/slicejs-cli/client.js modify"
|
|
19
|
-
},
|
|
20
|
-
"keywords": [],
|
|
21
|
-
"author": "",
|
|
22
|
-
"license": "ISC",
|
|
23
|
-
"type": "module",
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"express": "^4.19.2"
|
|
26
|
-
},
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"prettier": "3.3.3"
|
|
29
|
-
},
|
|
30
|
-
"prettier": {
|
|
31
|
-
"trailingComma": "es5",
|
|
32
|
-
"tabWidth": 3,
|
|
33
|
-
"semi": true,
|
|
34
|
-
"singleQuote": true,
|
|
35
|
-
"printWidth": 120
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "slicejs-web-framework",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=20"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
10
|
+
"slice:init": "node node_modules/slicejs-cli/client.js init",
|
|
11
|
+
"slice:create": "node node_modules/slicejs-cli/client.js component create",
|
|
12
|
+
"slice:delete": "node node_modules/slicejs-cli/client.js component delete",
|
|
13
|
+
"slice:list": "node node_modules/slicejs-cli/client.js component list",
|
|
14
|
+
"slice:start": "node api/index.js",
|
|
15
|
+
"format": "prettier --write \"**/*.js\"",
|
|
16
|
+
"run": "node api/index.js",
|
|
17
|
+
"development": "npm run",
|
|
18
|
+
"slice:modify": "node node_modules/slicejs-cli/client.js modify"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"express": "^4.19.2"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"prettier": "3.3.3"
|
|
29
|
+
},
|
|
30
|
+
"prettier": {
|
|
31
|
+
"trailingComma": "es5",
|
|
32
|
+
"tabWidth": 3,
|
|
33
|
+
"semi": true,
|
|
34
|
+
"singleQuote": true,
|
|
35
|
+
"printWidth": 120
|
|
36
|
+
}
|
|
37
|
+
}
|
package/sliceConfig.schema.json
CHANGED
package/src/App/index.html
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Slice - Documentation</title>
|
|
7
|
-
<link rel="icon" type="image/x-icon" href="/../../images/favicon.ico" />
|
|
8
|
-
<link rel="stylesheet" type="text/css" href="/App/style.css" />
|
|
9
|
-
|
|
10
|
-
<!-- Prism -->
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<!-- Eliminar Prism despues-->
|
|
15
|
-
</head>
|
|
16
|
-
|
|
17
|
-
<body>
|
|
18
|
-
<div id="app">
|
|
19
|
-
<script src="/App/index.js" type="module"></script>
|
|
20
|
-
</div>
|
|
21
|
-
</body>
|
|
22
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Slice - Documentation</title>
|
|
7
|
+
<link rel="icon" type="image/x-icon" href="/../../images/favicon.ico" />
|
|
8
|
+
<link rel="stylesheet" type="text/css" href="/App/style.css" />
|
|
9
|
+
|
|
10
|
+
<!-- Prism -->
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
<!-- Eliminar Prism despues-->
|
|
15
|
+
</head>
|
|
16
|
+
|
|
17
|
+
<body>
|
|
18
|
+
<div id="app">
|
|
19
|
+
<script src="/App/index.js" type="module"></script>
|
|
20
|
+
</div>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|