File size: 7,086 Bytes
30c32c8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
class ProjectPermissionManager {
    static permissions = {
        javascript: true,
        camera: true,
        allWebsites: true,
        cameraPictures: false,
        scratchSignIn: true,

        limitedWebsites: {}
    };
    static permissionMessages = {
        javascript: "use custom code in JavaScript",
        camera: "turn on your camera",
        allWebsites: "display all websites",
        cameraPictures: "take screenshots while your camera is on",
        scratchSignIn: "sign in with Scratch",

        limitedWebsite: "display $0",
    };
    static permissionDrawbacks = {
        javascript: [],
        camera: [],
        allWebsites: [],
        cameraPictures: ["take a screenshot while your camera is visible"],
        scratchSignIn: [],

        limitedWebsite: [],
    };
    static requiresCode = [];
    static disabledPermissions = [];

    static get skipPermissionRequest() {
        if (!vm) return false;
        if (!vm.runtime) return false;
        return vm.runtime.isProjectPermissionManagerDisabled === true;
    }

    static GenerateCode(length) {
        return Array.from(new Array(length).keys()).map(() => { return Math.round(Math.random() * 9) }).join(""); // generates something like 281964
    }
    static EditPromptForAcceptCheck(string) {
        return String(string).replace(/ /gmi, "").toLowerCase();
    }
    static RequestPermission(name, ...args) {
        // packager
        if (ProjectPermissionManager.skipPermissionRequest === true) return true;

        if (ProjectPermissionManager.disabledPermissions.includes(name)) return false;

        if (name == "limitedWebsite") {
            if (args.length < 1) throw new Error("No URL specified what are you trying to get permission for bro");
            if (!ProjectPermissionManager.IsUrlSafe(args[0])) return false;
        };

        // check if we already gave permission to do this
        if (name == "limitedWebsite") {
            if (ProjectPermissionManager.permissions.limitedWebsites[args[0]] == true) return true;
        };
        if (ProjectPermissionManager.permissions[name]) return true;

        let string = `Allow this project to ${ProjectPermissionManager.permissionMessages[name]}?`;
        for (let i = 0; i < args.length; i++) {
            const argument = args[i];
            string = string.replace(`$${i}`, (ProjectPermissionManager.IsDataUrl(argument) ? "custom website" : String(argument)));
        }

        string += `\n\nThis will allow the project to:\n`;
        ProjectPermissionManager.permissionDrawbacks[name].forEach(drawback => {
            string += `▪ ${drawback}\n`;
        });
        let acceptCode = "ok";
        if (ProjectPermissionManager.requiresCode.includes(name)) {
            acceptCode = ProjectPermissionManager.GenerateCode(6);
        };
        string += `\nType "${acceptCode}" to allow, or type "stop" to never ask for this permission again.`;

        const allow = ProjectPermissionManager.EditPromptForAcceptCheck(prompt(string, ""));
        if (allow == "stop") {
            // yes this does intentionally disable asking for any specific website after one was rejected
            ProjectPermissionManager.disabledPermissions.push(name);
            return false;
        }
        const allowed = allow === String(acceptCode);
        if (name == "limitedWebsite") {
            if (!allowed) return false;
            ProjectPermissionManager.permissions.limitedWebsites[args[0]] = true;
            return true;
        };
        ProjectPermissionManager.permissions[name] = allowed;
        return allowed;
    };
    static RequestAllPermissions() {
        // packager
        if (ProjectPermissionManager.skipPermissionRequest === true) return true;

        if (ProjectPermissionManager.disabledPermissions.includes("all")) return false;
        const permissions = [];
        Object.getOwnPropertyNames(ProjectPermissionManager.permissions).forEach(permissionName => {
            if (typeof ProjectPermissionManager.permissions[permissionName] != "boolean") return;
            permissions.push(permissionName);
        });

        let string = `Give all permissions to this project?`;

        string += `\n\nThis will allow the project to:\n`;
        permissions.forEach(permissionName => {
            ProjectPermissionManager.permissionDrawbacks[permissionName].forEach(drawback => {
                string += `▪ ${drawback}\n`;
            });
        });
        const acceptCode = ProjectPermissionManager.GenerateCode(8);
        string += `\nType "${acceptCode}" to allow, or type "stop" to never ask for all permissions again.`;

        const allow = ProjectPermissionManager.EditPromptForAcceptCheck(prompt(string, ""));
        if (allow == "stop") {
            ProjectPermissionManager.disabledPermissions.push("all");
            return false;
        }
        const allowed = allow === String(acceptCode);
        if (!allowed) return false;
        Object.getOwnPropertyNames(ProjectPermissionManager.permissions).forEach(permissionName => {
            if (typeof ProjectPermissionManager.permissions[permissionName] != "boolean") return;
            ProjectPermissionManager.permissions[permissionName] = true;
        });
        return true;
    }
    static CanCreateURLObject(url) {
        let success = true;
        try {
            new URL(url);
        } catch {
            success = false;
        }
        return success;
    }
    static IsDataUrl(url) {
        // if its not a valid url
        // its probably not a data url
        if (!this.CanCreateURLObject(url)) return false;
        // create url object cuz ez to read
        const urlObject = new URL(url);
        // now we can just check the protocol
        return urlObject.protocol === 'data:';
    };
    static IsUrlSafe(url) { // checks for non-kid friendly urls because that would be a big stinker
        // we dont know what this is just say yup thas good
        if (!this.CanCreateURLObject(url)) return false;
        // custom urls cannot be checked yet, just say its safe for now
        if (ProjectPermissionManager.IsDataUrl(url)) return true;
        const urlObject = new URL(url);
        const origin = urlObject.origin.toLowerCase();
        // check origin for stuff
        let returningValue = true;
        // obviously this can be bypassed
        // but it blocks large or well-known sites
        // from working straight out of the gate
        // projects with these sites will not be approved anyways
        if (
            origin.includes("xxx")
            || origin.includes("adult")
            || origin.includes(atob("c2V4"))
            || origin.includes(atob("cG9ybg=="))
            || origin.includes(atob("Ym9vcnU="))
            || origin.includes(atob("aGVudGFp"))
        ) returningValue = false;
        // const mainName = link.match(/(?=(\.|\/\/))[^\n]+(?=(\.))/gmi)[0].replace(/(\/\/|)[^\n]+(?=(\.))/gmi, "").replace(/\/\//gmi, "")
        return returningValue;
    };
};

module.exports = ProjectPermissionManager;