slicejs-web-framework 2.2.6 → 2.2.7

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/api/index.js CHANGED
@@ -1,15 +1,19 @@
1
+ // api/index.js - Seguridad automática sin configuración
1
2
  import express from 'express';
2
3
  import path from 'path';
3
4
  import { fileURLToPath } from 'url';
4
5
  import { dirname } from 'path';
5
- import inquirer from 'inquirer';
6
+ import {
7
+ securityMiddleware,
8
+ sliceFrameworkProtection,
9
+ suspiciousRequestLogger
10
+ } from './middleware/securityMiddleware.js';
6
11
 
7
12
  const __filename = fileURLToPath(import.meta.url);
8
13
  const __dirname = dirname(__filename);
9
14
  import sliceConfig from '../src/sliceConfig.json' with { type: 'json' };
10
15
 
11
16
  let server;
12
-
13
17
  const app = express();
14
18
 
15
19
  // Parsear argumentos de línea de comandos
@@ -22,12 +26,39 @@ const folderDeployed = 'src';
22
26
  // Obtener puerto desde sliceConfig.json, con fallback a process.env.PORT
23
27
  const PORT = sliceConfig.server?.port || process.env.PORT || 3001;
24
28
 
25
-
26
- app.use('/Slice/', express.static(path.join(__dirname, '..', 'node_modules', 'slicejs-web-framework', 'Slice')));
27
-
28
-
29
- // Middleware para servir archivos estáticos
30
- app.use(express.static(path.join(__dirname, `../${folderDeployed}`)));
29
+ // ==============================================
30
+ // MIDDLEWARES DE SEGURIDAD (APLICAR PRIMERO)
31
+ // ==============================================
32
+
33
+ // 1. Logger de peticiones sospechosas (solo observación, no bloquea)
34
+ app.use(suspiciousRequestLogger());
35
+
36
+ // 2. Protección del framework - TOTALMENTE AUTOMÁTICA
37
+ // Detecta automáticamente el dominio desde los headers
38
+ // Funciona en localhost, IP, y cualquier dominio
39
+ app.use(sliceFrameworkProtection());
40
+
41
+ // 3. Middleware de seguridad general
42
+ app.use(securityMiddleware({
43
+ allowedExtensions: [
44
+ '.js', '.css', '.html', '.json',
45
+ '.svg', '.png', '.jpg', '.jpeg', '.gif',
46
+ '.woff', '.woff2', '.ttf', '.ico'
47
+ ],
48
+ blockedPaths: [
49
+ '/node_modules',
50
+ '/package.json',
51
+ '/package-lock.json',
52
+ '/.env',
53
+ '/.git',
54
+ '/api/middleware'
55
+ ],
56
+ allowPublicAssets: true
57
+ }));
58
+
59
+ // ==============================================
60
+ // MIDDLEWARES DE APLICACIÓN
61
+ // ==============================================
31
62
 
32
63
  // Middleware para parsear JSON y formularios
33
64
  app.use(express.json());
@@ -46,6 +77,20 @@ app.use((req, res, next) => {
46
77
  }
47
78
  });
48
79
 
80
+ // ==============================================
81
+ // ARCHIVOS ESTÁTICOS (DESPUÉS DE SEGURIDAD)
82
+ // ==============================================
83
+
84
+ // Servir framework Slice.js
85
+ app.use('/Slice/', express.static(path.join(__dirname, '..', 'node_modules', 'slicejs-web-framework', 'Slice')));
86
+
87
+ // Servir archivos estáticos del proyecto
88
+ app.use(express.static(path.join(__dirname, `../${folderDeployed}`)));
89
+
90
+ // ==============================================
91
+ // RUTAS DE API
92
+ // ==============================================
93
+
49
94
  // Ruta de ejemplo para API
50
95
  app.get('/api/status', (req, res) => {
51
96
  res.json({
@@ -54,13 +99,57 @@ app.get('/api/status', (req, res) => {
54
99
  folder: folderDeployed,
55
100
  timestamp: new Date().toISOString(),
56
101
  framework: 'Slice.js',
57
- version: '2.0.0'
102
+ version: '2.0.0',
103
+ security: {
104
+ enabled: true,
105
+ mode: 'automatic',
106
+ description: 'Zero-config security - works with any domain'
107
+ }
108
+ });
109
+ });
110
+
111
+ // Ruta para verificar estado de seguridad
112
+ app.get('/api/security-status', (req, res) => {
113
+ const host = req.get('Host');
114
+
115
+ res.json({
116
+ frameworkProtection: {
117
+ status: 'active',
118
+ mode: 'automatic',
119
+ description: 'Allows framework file loading from application, blocks direct browser access',
120
+ detectedHost: host
121
+ },
122
+ blockedPaths: [
123
+ '/node_modules/*',
124
+ '/package.json',
125
+ '/.env',
126
+ '/.git/*'
127
+ ],
128
+ protectedPaths: {
129
+ directAccessBlocked: [
130
+ '/Slice/Components/Structural/*',
131
+ '/Slice/Core/*',
132
+ '/Slice/Services/*'
133
+ ],
134
+ allowedFrom: 'Any request with valid Referer matching current host'
135
+ },
136
+ howItWorks: {
137
+ automatic: true,
138
+ config: 'No configuration needed',
139
+ localhost: 'Works automatically',
140
+ customDomain: 'Works automatically',
141
+ detection: 'Uses Referer and Host headers'
142
+ }
58
143
  });
59
144
  });
60
145
 
146
+ // ==============================================
147
+ // SPA FALLBACK
148
+ // ==============================================
149
+
61
150
  // SPA fallback - servir index.html para rutas no encontradas
62
151
  app.get('*', (req, res) => {
63
- const indexPath = path.join(__dirname, `../${folderDeployed}`,"App", 'index.html');
152
+ const indexPath = path.join(__dirname, `../${folderDeployed}`, "App", 'index.html');
64
153
  res.sendFile(indexPath, (err) => {
65
154
  if (err) {
66
155
  res.status(404).send(`
@@ -76,8 +165,14 @@ app.get('*', (req, res) => {
76
165
  });
77
166
  });
78
167
 
168
+ // ==============================================
169
+ // INICIO DEL SERVIDOR
170
+ // ==============================================
171
+
79
172
  function startServer() {
80
173
  server = app.listen(PORT, () => {
174
+ console.log(`🔒 Security middleware: active (zero-config, automatic)`);
175
+ console.log(`🚀 Slice.js server running on port ${PORT}`);
81
176
  });
82
177
  }
83
178
 
@@ -95,4 +190,4 @@ process.on('SIGTERM', () => {
95
190
  // Iniciar servidor
96
191
  startServer();
97
192
 
98
- export default app;
193
+ export default app;
@@ -0,0 +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
253
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slicejs-web-framework",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=20"