• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / roku-debug / #1977

pending completion
#1977

push

TwitchBronBron
Fix issue with truncated debugger paths

999 of 1900 branches covered (52.58%)

Branch coverage included in aggregate %.

2082 of 3571 relevant lines covered (58.3%)

16.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.73
/src/ComponentLibraryServer.ts
1
import * as fs from 'fs';
1✔
2
import * as http from 'http';
1✔
3
import * as os from 'os';
1✔
4
import * as path from 'path';
1✔
5
import * as url from 'url';
1✔
6

7
import { util } from './util';
1✔
8

9
export class ComponentLibraryServer {
1✔
10

11
    public componentLibrariesOutDir: string;
12

13
    private server: http.Server;
14

15
    public async startStaticFileHosting(componentLibrariesOutDir: string, port: number, sendDebugLogLine) {
16

17
        // Make sure the requested port is not already being used by another service
18
        if (await util.isPortInUse(port)) {
×
19
            throw new Error(`Could not host component library files.\nPort ${port} is currently occupied.`);
×
20
        }
21

22
        this.componentLibrariesOutDir = componentLibrariesOutDir;
×
23

24
        // prepare static file hosting
25
        // maps file extension to MIME types
26
        const mimeType = {
×
27
            '.ico': 'image/x-icon',
28
            '.html': 'text/html',
29
            '.js': 'text/javascript',
30
            '.json': 'application/json',
31
            '.css': 'text/css',
32
            '.png': 'image/png',
33
            '.jpg': 'image/jpeg',
34
            '.wav': 'audio/wav',
35
            '.mp3': 'audio/mpeg',
36
            '.svg': 'image/svg+xml',
37
            '.pdf': 'application/pdf',
38
            '.doc': 'application/msword',
39
            '.eot': 'appliaction/vnd.ms-fontobject',
40
            '.ttf': 'aplication/font-sfnt',
41
            '.zip': 'application/zip'
42
        } as Record<string, string>;
43

44
        this.server = http.createServer((req, res) => {
×
45
            sendDebugLogLine(`${req.method} ${req.url}`);
×
46

47
            // parse URL
48
            const parsedUrl = url.parse(req.url);
×
49

50
            // extract URL path
51
            // Avoid https://en.wikipedia.org/wiki/Directory_traversal_attack
52
            // e.g curl --path-as-is http://localhost:9000/../fileInDanger.txt
53
            // by limiting the path to component libraries out directory only
54
            const sanitizePath = path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '');
×
55
            let pathname = path.join(this.componentLibrariesOutDir, sanitizePath);
×
56

57
            fs.exists(pathname, (exist) => {
×
58
                if (!exist) {
×
59
                    // if the file is not found, return 404
60
                    res.statusCode = 404;
×
61
                    res.end(`File ${pathname} not found!`);
×
62
                    return;
×
63
                }
64

65
                // if is a directory
66
                // if (fs.statSync(pathname).isDirectory()) {
67
                // TODO: add support for directory listing
68
                // }
69

70
                // read file from file system
71
                fs.readFile(pathname, (err, data) => {
×
72
                    if (err) {
×
73
                        res.statusCode = 500;
×
74
                        res.end(`Error getting the file: ${err}.`);
×
75
                    } else {
76
                        // based on the URL path, extract the file extension. e.g. .js, .doc, ...
77
                        const ext = path.parse(pathname).ext;
×
78
                        // if the file is found, set Content-type and send data
79
                        res.setHeader('Content-type', mimeType[ext] || 'text/plain');
×
80
                        res.end(data);
×
81
                    }
82
                });
83
            });
84

85
        }).listen(port);
86

87
        sendDebugLogLine(`Server listening on port ${port}`);
×
88

89
        // print possible IP addresses that may be the users local ip
90
        let ifaces = os.networkInterfaces();
×
91
        for (const ifname of Object.keys(ifaces)) {
×
92
            for (const iface of ifaces[ifname]) {
×
93
                if (iface.family !== 'IPv4' || iface.internal !== false) {
×
94
                    // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
95
                    return;
×
96
                }
97

98
                sendDebugLogLine(`Potential target ip for component libraries: ${iface.address}`);
×
99
            }
100
        }
101
    }
102

103
    /**
104
     * Stop the server (if it's running)
105
     */
106
    public stop() {
107
        if (this.server) {
×
108
            this.server.close();
×
109
        }
110
    }
111
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc