AI Hacks : Locked down kids browser

AI Hacks : Locked down kids browser

This year my daughter started coming home with homework that she could access “online”. This also coincided with her starting “computer club” at school. As I had a spare Linux machine I gave her this to use and got her logged into “Sumdog” the gamified maths-based online game / social network.

After about a week my wife questioned whether the machine was “locked down” at all. Now my daughter is [at least for now] very good and doesn’t go off and try things without asking but sure, an oversight on my part, so I went to apply some parental settings. This though wasn’t quite as straight forward as you’d imagine. Ubuntu does not in fact support “child” profiles and being Linux most of the configuration and customisation is command-line based and / or something that requires research.

After what seemed like half my life, but what was probably in fact an hour or so, I got fed up of the different suggestions of DNS whitelisting settings that didn’t sync with the home network and Ubuntu distribution version quirks and was about to rage-quit it all until I remembered to go back to basics - what was it I was trying to do - lock down the browser. The browser being the issue, and as Firefox on Linux didn’t provide the flexibility or heightened restrictions required, I needed a browser that did.

Now Chrome does afford a lot of this kind of thing but I am loathe to provide Google with any more personal information than it already has from my now rarely used gmail account. Monitoring my daughter’s proficiency in maths so they can run targeted ads isn’t something I wanted to add.

So what to do when you can barely trust any web browser manufacturers with your own privacy? Well build your own of course!

ChatGPT has entered the chat. 

Given Chromium is the open-source browser that many browsers are built on I asked ChatGPT to give me a web browser that I could open like any normal application that has no url bar, 3 buttons that navigate to only the following sites [Sumdog / Google Classroom / CBeebies], that opens full-screen with no minimise buttons and a keyboard combination of “ctrl-shift-e” to exit.

Here’s the code! *caveat, you’ll need to mess about with having python installed and battle with chmod settings to allow for execution but again - that’s an AI search away.

#!/usr/bin/env python3
"""
Kids Browser – child-safe launcher
-----------------------------------
• Full-screen browser with preset buttons
• Each button opens its own hidden tab (keeps session)
• Allows popups/new tabs internally (no visible tab bar)
• Secret exit combo: Ctrl + Shift + E
"""

import sys
import os
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTabWidget
)
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEnginePage
from PyQt6.QtCore import QUrl, Qt


class CustomWebEnginePage(QWebEnginePage):
    """Handles popups or new tabs inside hidden tab structure."""
    def createWindow(self, _type):
        new_view = QWebEngineView()
        new_page = CustomWebEnginePage(new_view)
        new_view.setPage(new_page)
        # Add the popup as a new hidden tab
        parent_browser = self.parent().parent()
        parent_browser.add_tab(new_view)
        return new_view


class SafeBrowser(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Connie Browser")

        # Hide window frame safely across PyQt6 versions
        try:
            self.setWindowFlags(Qt.FramelessWindowHint)
        except AttributeError:
            self.setWindowFlags(Qt.WindowType.FramelessWindowHint)

        self.showFullScreen()

        layout = QVBoxLayout()
        self.setLayout(layout)

        # --- Button bar ---
        button_bar = QHBoxLayout()
        layout.addLayout(button_bar)

        self.sites = {
            "CBeebies": "https://www.bbc.co.uk/cbeebies",
            "Google Classroom": "https://classroom.google.com",
            "YouTube Kids": "https://www.youtubekids.com",
            "ChatGPT": "https://chat.openai.com",
            "Sumdog": "https://play.sumdog.com"
        }

        # --- Hidden tab manager ---
        self.tabs = QTabWidget()
        self.tabs.setTabsClosable(False)
        self.tabs.setMovable(False)
        self.tabs.tabBar().hide()
        layout.addWidget(self.tabs)

        # Create a tab for each site (keeps session)
        for name, url in self.sites.items():
            webview = QWebEngineView()
            page = CustomWebEnginePage(webview)
            webview.setPage(page)
            webview.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
            webview.setUrl(QUrl(url))
            self.tabs.addTab(webview, name)

            # Make a button for it
            btn = QPushButton(name)
            btn.setStyleSheet("""
                QPushButton {
                    font-size: 18px;
                    padding: 12px 18px;
                    border-radius: 8px;
                    background-color: #007AFF;
                    color: white;
                }
                QPushButton:hover {
                    background-color: #005BBB;
                }
            """)
            btn.clicked.connect(lambda _, n=name: self.show_tab(n))
            button_bar.addWidget(btn)

        # Start with the first site visible
        self.tabs.setCurrentIndex(0)

    def show_tab(self, site_name):
        """Switch to an already-loaded tab (no reload)."""
        for i in range(self.tabs.count()):
            if self.tabs.tabText(i) == site_name:
                self.tabs.setCurrentIndex(i)
                break

    def add_tab(self, webview):
        """Add new hidden tab (used for popups/new windows)."""
        self.tabs.addTab(webview, "")
        self.tabs.setCurrentWidget(webview)

    def keyPressEvent(self, event):
        """Secret exit combo: Ctrl + Shift + E"""
        if (
            event.key() == Qt.Key.Key_E
            and event.modifiers() & Qt.KeyboardModifier.ControlModifier
            and event.modifiers() & Qt.KeyboardModifier.ShiftModifier
        ):
            QApplication.quit()


def main():
    os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--no-sandbox"
    app = QApplication(sys.argv)
    browser = SafeBrowser()
    browser.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()

With setting the machine to auto-login to my daughter’s profile and the only icon on the dock being the “kids browser” and it’s been highly effective. It also solved an issue that I didn’t have loads of time to dedicate to. With her curiosity and skills with computers set to grow I am sure this is not a long-term solution, but it doesn’t have to be. Just needs to plug the hole until the next thing comes along.

You can do the same on any OS, and the code should be pretty lightweight.

So maybe in not locking it down in the first place I accidentally made her environment even more security focussed - a fully locked down, privacy-focused browser for homework. Maybe it’ll catch on!

Analogue Sheep : Vol 10 : The Coldest War

Analogue Sheep : Vol 10 : The Coldest War