nyte 1.2.1 → 1.2.3

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.
@@ -280,9 +280,11 @@ function DevIndicator({ hasBuildError = false, onClickBuildError, }) {
280
280
  }
281
281
  .nyte-dev-badge {
282
282
  /* AQUI ESTÁ O QUE SEGURA ELE NA TELA: */
283
- position: fixed;
283
+ position: sticky;
284
284
  bottom: 20px;
285
- right: 20px;
285
+ /* float e margens para forçar a direita no modo sticky */
286
+ float: right;
287
+ margin-right: 20px;
286
288
  z-index: 999999;
287
289
 
288
290
  display: flex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nyte",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Nyte.js is a high-level framework for building web applications with ease and speed. It provides a robust set of tools and features to streamline development and enhance productivity.",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "itsmuzin",
@@ -29,6 +29,11 @@
29
29
  "import": "./dist/index.js",
30
30
  "require": "./dist/index.js"
31
31
  },
32
+ "./console": {
33
+ "types": "./dist/api/console.d.ts",
34
+ "import": "./dist/api/console.js",
35
+ "require": "./dist/api/console.js"
36
+ },
32
37
  "./react": {
33
38
  "types": "./dist/client/client.d.ts",
34
39
  "import": "./dist/client/client.js",
@@ -261,10 +261,12 @@ function App({ componentMap, routes, initialComponentPath, initialParams, layout
261
261
 
262
262
 
263
263
 
264
+
265
+
264
266
  export function DevIndicator({
265
- hasBuildError = false,
266
- onClickBuildError,
267
- }: {
267
+ hasBuildError = false,
268
+ onClickBuildError,
269
+ }: {
268
270
  hasBuildError?: boolean;
269
271
  onClickBuildError?: () => void;
270
272
  }) {
@@ -314,9 +316,11 @@ export function DevIndicator({
314
316
  }
315
317
  .nyte-dev-badge {
316
318
  /* AQUI ESTÁ O QUE SEGURA ELE NA TELA: */
317
- position: fixed;
319
+ position: sticky;
318
320
  bottom: 20px;
319
- right: 20px;
321
+ /* float e margens para forçar a direita no modo sticky */
322
+ float: right;
323
+ margin-right: 20px;
320
324
  z-index: 999999;
321
325
 
322
326
  display: flex;
@@ -1 +0,0 @@
1
- export {};
@@ -1,170 +0,0 @@
1
- "use strict";
2
- /*
3
- * Lightweight security self-tests (no external test runner required).
4
- *
5
- * These tests validate that:
6
- * - Static serving does NOT allow path traversal outside its base dirs.
7
- * - HTTP->HTTPS redirect does NOT trust hostile Host headers.
8
- *
9
- * Run (from repo root):
10
- * node packages/nytejs/dist/security.selftest.js
11
- */
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- const fs_1 = __importDefault(require("fs"));
17
- const path_1 = __importDefault(require("path"));
18
- const http_1 = __importDefault(require("http"));
19
- const helpers_1 = require("./helpers");
20
- function assert(condition, message) {
21
- if (!condition)
22
- throw new Error(message);
23
- }
24
- async function httpRequest(opts) {
25
- return new Promise((resolve, reject) => {
26
- const req = http_1.default.request(opts, (res) => {
27
- let data = '';
28
- res.setEncoding('utf8');
29
- res.on('data', (c) => (data += c));
30
- res.on('end', () => resolve({ status: res.statusCode || 0, headers: res.headers, body: data }));
31
- });
32
- req.on('error', reject);
33
- if (opts.body)
34
- req.write(opts.body);
35
- req.end();
36
- });
37
- }
38
- function randomPort() {
39
- // Avoid privileged ports. Not cryptographic; just for tests.
40
- return 20000 + Math.floor(Math.random() * 20000);
41
- }
42
- // --- Redirect host poisoning: test a pure builder (no TLS required) ---
43
- function buildHttpsRedirectLocation(params) {
44
- const rawHost = String(params.requestHostHeader || '').trim();
45
- let hostToUse = String(params.configuredHostname || params.fallbackHostname || '').trim();
46
- if (!hostToUse) {
47
- const hostWithoutPort = rawHost.split(':')[0];
48
- const isValidHost = /^[a-zA-Z0-9.-]+$/.test(hostWithoutPort) && !hostWithoutPort.includes('..');
49
- hostToUse = isValidHost ? hostWithoutPort : 'localhost';
50
- }
51
- let redirectUrl = `https://${hostToUse}`;
52
- if (params.httpsPort !== 443)
53
- redirectUrl += `:${params.httpsPort}`;
54
- redirectUrl += params.requestUrl || '/';
55
- return redirectUrl;
56
- }
57
- async function withTempProject(fn) {
58
- const base = path_1.default.join(process.cwd(), '.nyte-security-selftest');
59
- fs_1.default.mkdirSync(base, { recursive: true });
60
- const projectDir = fs_1.default.mkdtempSync(path_1.default.join(base, 'proj-'));
61
- // minimal folder structure
62
- fs_1.default.mkdirSync(path_1.default.join(projectDir, 'src', 'web', 'routes'), { recursive: true });
63
- fs_1.default.mkdirSync(path_1.default.join(projectDir, 'src', 'backend', 'routes'), { recursive: true });
64
- fs_1.default.mkdirSync(path_1.default.join(projectDir, 'public'), { recursive: true });
65
- // Basic route so app.prepare() succeeds (route loader expects files)
66
- const routeFile = path_1.default.join(projectDir, 'src', 'web', 'routes', 'index.tsx');
67
- fs_1.default.writeFileSync(routeFile, `export default function Index(){ return null; }\nexport const component = Index;\n`, 'utf8');
68
- // A secret file OUTSIDE public/.nyte to test traversal attempts
69
- fs_1.default.writeFileSync(path_1.default.join(projectDir, 'SECRET.txt'), 'TOP_SECRET', 'utf8');
70
- try {
71
- return await fn(projectDir);
72
- }
73
- finally {
74
- try {
75
- fs_1.default.rmSync(projectDir, { recursive: true, force: true });
76
- }
77
- catch {
78
- // ignore cleanup issues
79
- }
80
- }
81
- }
82
- async function testStaticPathTraversal() {
83
- await withTempProject(async (projectDir) => {
84
- const port = randomPort();
85
- const server = await (0, helpers_1.app)({ dir: projectDir, dev: true, port, hostname: '127.0.0.1' }).init();
86
- try {
87
- // Try to escape public dir
88
- const res1 = await httpRequest({
89
- hostname: '127.0.0.1',
90
- port,
91
- method: 'GET',
92
- path: '/../SECRET.txt'
93
- });
94
- assert(res1.status !== 200, `Expected traversal from public to be blocked, got 200 with body: ${res1.body}`);
95
- assert(!res1.body.includes('TOP_SECRET'), 'Traversal leaked secret content');
96
- // Try to escape .nyte via /_nyte/
97
- const res2 = await httpRequest({
98
- hostname: '127.0.0.1',
99
- port,
100
- method: 'GET',
101
- path: '/_nyte/../SECRET.txt'
102
- });
103
- assert(res2.status !== 200, `Expected traversal from /_nyte to be blocked, got 200 with body: ${res2.body}`);
104
- assert(!res2.body.includes('TOP_SECRET'), 'Traversal via /_nyte leaked secret content');
105
- // Encoded traversal
106
- const res3 = await httpRequest({
107
- hostname: '127.0.0.1',
108
- port,
109
- method: 'GET',
110
- path: '/_nyte/%2e%2e/SECRET.txt'
111
- });
112
- assert(res3.status !== 200, `Expected encoded traversal to be blocked, got 200 with body: ${res3.body}`);
113
- assert(!res3.body.includes('TOP_SECRET'), 'Encoded traversal leaked secret content');
114
- }
115
- finally {
116
- await new Promise((resolve) => server.close(() => resolve()));
117
- }
118
- });
119
- }
120
- async function testHttpsRedirectHostPoisoning() {
121
- // configuredHostname should win over hostile Host header
122
- const loc1 = buildHttpsRedirectLocation({
123
- configuredHostname: 'example.com',
124
- fallbackHostname: '0.0.0.0',
125
- httpsPort: 8443,
126
- requestHostHeader: 'evil.com',
127
- requestUrl: '/login?x=1'
128
- });
129
- assert(loc1.startsWith('https://example.com:8443/'), `Expected configured hostname, got ${loc1}`);
130
- assert(!loc1.includes('evil.com'), `Redirect trusted hostile Host header. Location=${loc1}`);
131
- // without configured host, a valid host header is accepted
132
- const loc2 = buildHttpsRedirectLocation({
133
- httpsPort: 443,
134
- requestHostHeader: 'good.example',
135
- requestUrl: '/'
136
- });
137
- assert(loc2 === 'https://good.example/', `Expected host header to be used, got ${loc2}`);
138
- // invalid host header should be rejected
139
- const loc3 = buildHttpsRedirectLocation({
140
- httpsPort: 443,
141
- requestHostHeader: 'evil.com\r\nX-Evil: 1',
142
- requestUrl: '/'
143
- });
144
- assert(loc3 === 'https://localhost/', `Expected invalid host to fallback to localhost, got ${loc3}`);
145
- }
146
- async function main() {
147
- const results = [];
148
- async function run(name, fn) {
149
- try {
150
- await fn();
151
- results.push({ name, ok: true });
152
- }
153
- catch (e) {
154
- results.push({ name, ok: false, error: e?.message || String(e) });
155
- }
156
- }
157
- await run('static path traversal', testStaticPathTraversal);
158
- await run('https redirect host poisoning', testHttpsRedirectHostPoisoning);
159
- const failed = results.filter(r => !r.ok);
160
- for (const r of results) {
161
- // eslint-disable-next-line no-console
162
- console.log(`${r.ok ? 'PASS' : 'FAIL'} - ${r.name}${r.ok ? '' : `: ${r.error}`}`);
163
- }
164
- if (failed.length) {
165
- process.exitCode = 1;
166
- }
167
- }
168
- // Only run when executed directly
169
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
170
- main();
@@ -1,192 +0,0 @@
1
- /*
2
- * Lightweight security self-tests (no external test runner required).
3
- *
4
- * These tests validate that:
5
- * - Static serving does NOT allow path traversal outside its base dirs.
6
- * - HTTP->HTTPS redirect does NOT trust hostile Host headers.
7
- *
8
- * Run (from repo root):
9
- * node packages/nytejs/dist/security.selftest.js
10
- */
11
-
12
- import fs from 'fs';
13
- import path from 'path';
14
- import http from 'http';
15
- import { app } from './helpers';
16
-
17
- function assert(condition: any, message: string): void {
18
- if (!condition) throw new Error(message);
19
- }
20
-
21
- async function httpRequest(opts: http.RequestOptions & { body?: string }): Promise<{ status: number; headers: http.IncomingHttpHeaders; body: string }> {
22
- return new Promise((resolve, reject) => {
23
- const req = http.request(opts, (res) => {
24
- let data = '';
25
- res.setEncoding('utf8');
26
- res.on('data', (c) => (data += c));
27
- res.on('end', () => resolve({ status: res.statusCode || 0, headers: res.headers, body: data }));
28
- });
29
- req.on('error', reject);
30
- if (opts.body) req.write(opts.body);
31
- req.end();
32
- });
33
- }
34
-
35
- function randomPort(): number {
36
- // Avoid privileged ports. Not cryptographic; just for tests.
37
- return 20000 + Math.floor(Math.random() * 20000);
38
- }
39
-
40
- // --- Redirect host poisoning: test a pure builder (no TLS required) ---
41
- function buildHttpsRedirectLocation(params: {
42
- configuredHostname?: string;
43
- fallbackHostname?: string;
44
- httpsPort: number;
45
- requestHostHeader?: string;
46
- requestUrl?: string;
47
- }): string {
48
- const rawHost = String(params.requestHostHeader || '').trim();
49
- let hostToUse = String(params.configuredHostname || params.fallbackHostname || '').trim();
50
-
51
- if (!hostToUse) {
52
- const hostWithoutPort = rawHost.split(':')[0];
53
- const isValidHost = /^[a-zA-Z0-9.-]+$/.test(hostWithoutPort) && !hostWithoutPort.includes('..');
54
- hostToUse = isValidHost ? hostWithoutPort : 'localhost';
55
- }
56
-
57
- let redirectUrl = `https://${hostToUse}`;
58
- if (params.httpsPort !== 443) redirectUrl += `:${params.httpsPort}`;
59
- redirectUrl += params.requestUrl || '/';
60
- return redirectUrl;
61
- }
62
-
63
- async function withTempProject<T>(fn: (projectDir: string) => Promise<T>): Promise<T> {
64
- const base = path.join(process.cwd(), '.nyte-security-selftest');
65
- fs.mkdirSync(base, { recursive: true });
66
- const projectDir = fs.mkdtempSync(path.join(base, 'proj-'));
67
-
68
- // minimal folder structure
69
- fs.mkdirSync(path.join(projectDir, 'src', 'web', 'routes'), { recursive: true });
70
- fs.mkdirSync(path.join(projectDir, 'src', 'backend', 'routes'), { recursive: true });
71
- fs.mkdirSync(path.join(projectDir, 'public'), { recursive: true });
72
-
73
- // Basic route so app.prepare() succeeds (route loader expects files)
74
- const routeFile = path.join(projectDir, 'src', 'web', 'routes', 'index.tsx');
75
- fs.writeFileSync(
76
- routeFile,
77
- `export default function Index(){ return null; }\nexport const component = Index;\n`,
78
- 'utf8'
79
- );
80
-
81
- // A secret file OUTSIDE public/.nyte to test traversal attempts
82
- fs.writeFileSync(path.join(projectDir, 'SECRET.txt'), 'TOP_SECRET', 'utf8');
83
-
84
- try {
85
- return await fn(projectDir);
86
- } finally {
87
- try {
88
- fs.rmSync(projectDir, { recursive: true, force: true });
89
- } catch {
90
- // ignore cleanup issues
91
- }
92
- }
93
- }
94
-
95
- async function testStaticPathTraversal(): Promise<void> {
96
- await withTempProject(async (projectDir) => {
97
- const port = randomPort();
98
- const server = await app({ dir: projectDir, dev: true, port, hostname: '127.0.0.1' }).init();
99
- try {
100
- // Try to escape public dir
101
- const res1 = await httpRequest({
102
- hostname: '127.0.0.1',
103
- port,
104
- method: 'GET',
105
- path: '/../SECRET.txt'
106
- });
107
- assert(res1.status !== 200, `Expected traversal from public to be blocked, got 200 with body: ${res1.body}`);
108
- assert(!res1.body.includes('TOP_SECRET'), 'Traversal leaked secret content');
109
-
110
- // Try to escape .nyte via /_nyte/
111
- const res2 = await httpRequest({
112
- hostname: '127.0.0.1',
113
- port,
114
- method: 'GET',
115
- path: '/_nyte/../SECRET.txt'
116
- });
117
- assert(res2.status !== 200, `Expected traversal from /_nyte to be blocked, got 200 with body: ${res2.body}`);
118
- assert(!res2.body.includes('TOP_SECRET'), 'Traversal via /_nyte leaked secret content');
119
-
120
- // Encoded traversal
121
- const res3 = await httpRequest({
122
- hostname: '127.0.0.1',
123
- port,
124
- method: 'GET',
125
- path: '/_nyte/%2e%2e/SECRET.txt'
126
- });
127
- assert(res3.status !== 200, `Expected encoded traversal to be blocked, got 200 with body: ${res3.body}`);
128
- assert(!res3.body.includes('TOP_SECRET'), 'Encoded traversal leaked secret content');
129
- } finally {
130
- await new Promise<void>((resolve) => (server as any).close(() => resolve()));
131
- }
132
- });
133
- }
134
-
135
- async function testHttpsRedirectHostPoisoning(): Promise<void> {
136
- // configuredHostname should win over hostile Host header
137
- const loc1 = buildHttpsRedirectLocation({
138
- configuredHostname: 'example.com',
139
- fallbackHostname: '0.0.0.0',
140
- httpsPort: 8443,
141
- requestHostHeader: 'evil.com',
142
- requestUrl: '/login?x=1'
143
- });
144
- assert(loc1.startsWith('https://example.com:8443/'), `Expected configured hostname, got ${loc1}`);
145
- assert(!loc1.includes('evil.com'), `Redirect trusted hostile Host header. Location=${loc1}`);
146
-
147
- // without configured host, a valid host header is accepted
148
- const loc2 = buildHttpsRedirectLocation({
149
- httpsPort: 443,
150
- requestHostHeader: 'good.example',
151
- requestUrl: '/'
152
- });
153
- assert(loc2 === 'https://good.example/', `Expected host header to be used, got ${loc2}`);
154
-
155
- // invalid host header should be rejected
156
- const loc3 = buildHttpsRedirectLocation({
157
- httpsPort: 443,
158
- requestHostHeader: 'evil.com\r\nX-Evil: 1',
159
- requestUrl: '/'
160
- });
161
- assert(loc3 === 'https://localhost/', `Expected invalid host to fallback to localhost, got ${loc3}`);
162
- }
163
-
164
- async function main(): Promise<void> {
165
- const results: Array<{ name: string; ok: boolean; error?: string }> = [];
166
-
167
- async function run(name: string, fn: () => Promise<void>) {
168
- try {
169
- await fn();
170
- results.push({ name, ok: true });
171
- } catch (e: any) {
172
- results.push({ name, ok: false, error: e?.message || String(e) });
173
- }
174
- }
175
-
176
- await run('static path traversal', testStaticPathTraversal);
177
- await run('https redirect host poisoning', testHttpsRedirectHostPoisoning);
178
-
179
- const failed = results.filter(r => !r.ok);
180
- for (const r of results) {
181
- // eslint-disable-next-line no-console
182
- console.log(`${r.ok ? 'PASS' : 'FAIL'} - ${r.name}${r.ok ? '' : `: ${r.error}`}`);
183
- }
184
-
185
- if (failed.length) {
186
- process.exitCode = 1;
187
- }
188
- }
189
-
190
- // Only run when executed directly
191
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
192
- main();