Commit 7f7ae4e8 authored by Andreas Düren's avatar Andreas Düren
Browse files

Implement comprehensive SRP Buffer polyfill to fix verification errors

parent 62895778
Loading
Loading
Loading
Loading
+320 −56
Original line number Diff line number Diff line
@@ -679,8 +679,10 @@ chmod 644 /app/data/web/runtime-config.js

# Create a custom URL patch file to fix the URL constructor error
echo "==> Creating URL and SRP patch file"
cat << EOF > /app/data/web/ente-patches.js
cat > /app/data/web/photos/static/ente-patches.js << 'ENDPATCHES'
(function() {
    console.log('Applying Ente URL and SRP patches...');
    
    // Save original URL constructor
    const originalURL = window.URL;
    
@@ -710,102 +712,364 @@ cat << EOF > /app/data/web/ente-patches.js
        }
    };
    
    // More robust Buffer implementation for SRP
    window.Buffer = window.Buffer || {
    // Comprehensive Buffer polyfill for SRP
    const originalBuffer = window.Buffer;
    window.Buffer = {
        from: function(data, encoding) {
            // Handle undefined data - critical fix
            if (data === undefined) {
                console.warn('Buffer.from called with undefined data, creating empty buffer');
                return {
                    data: '',
            // Debug logging for the SRP calls
            console.debug('Buffer.from called with:', 
                typeof data, 
                data === undefined ? 'undefined' : 
                data === null ? 'null' : 
                Array.isArray(data) ? 'array[' + data.length + ']' : 
                'value', 
                'encoding:', encoding);
            
            // Handle undefined/null data - critical fix
            if (data === undefined || data === null) {
                console.warn('Buffer.from called with ' + (data === undefined ? 'undefined' : 'null') + ' data, creating empty buffer');
                const result = {
                    data: new Uint8Array(0),
                    length: 0,
                    toString: function() { return ''; }
                    toString: function(enc) { return ''; }
                };
                
                // Add additional methods that SRP might use
                result.slice = function() { return Buffer.from([]); };
                result.readUInt32BE = function() { return 0; };
                result.writeUInt32BE = function() { return result; };
                
                return result;
            }
            
            // Special case for hex strings - very important for SRP
            if (typeof data === 'string' && encoding === 'hex') {
                // Convert hex string to byte array
                const bytes = [];
                for (let i = 0; i < data.length; i += 2) {
                    if (data.length - i >= 2) {
                        bytes.push(parseInt(data.substr(i, 2), 16));
                    }
                }
                
                const result = {
                    data: new Uint8Array(bytes),
                    length: bytes.length,
                    toString: function(enc) {
                        if (enc === 'hex' || !enc) {
                            return data; // Return original hex string
                        }
                        return bytes.map(b => String.fromCharCode(b)).join('');
                    }
                };
                
                // Add methods needed by SRP
                result.slice = function(start, end) {
                    const slicedData = bytes.slice(start, end);
                    return Buffer.from(slicedData.map(b => b.toString(16).padStart(2, '0')).join(''), 'hex');
                };
                
                result.readUInt32BE = function(offset = 0) {
                    let value = 0;
                    for (let i = 0; i < 4; i++) {
                        value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
                    }
                    return value;
                };
                
                result.writeUInt32BE = function(value, offset = 0) {
                    for (let i = 0; i < 4; i++) {
                        if (offset + i < bytes.length) {
                            bytes[offset + 3 - i] = value & 0xFF;
                            value >>>= 8;
                        }
                    }
                    return result;
                };
                
                return result;
            }
            
            // Handle string data
            if (typeof data === 'string') {
                return {
                    data: data,
                    length: data.length,
                const bytes = Array.from(data).map(c => c.charCodeAt(0));
                const result = {
                    data: new Uint8Array(bytes),
                    length: bytes.length,
                    toString: function(enc) {
                        if (enc === 'hex' && encoding === 'hex') {
                            // Return the original hex string
                            return data;
                        if (enc === 'hex') {
                            return bytes.map(b => b.toString(16).padStart(2, '0')).join('');
                        }
                        return data;
                    }
                };
                
                // Add SRP methods
                result.slice = function(start, end) {
                    return Buffer.from(data.slice(start, end));
                };
                
                result.readUInt32BE = function(offset = 0) {
                    let value = 0;
                    for (let i = 0; i < 4; i++) {
                        value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
                    }
                    return value;
                };
                
                result.writeUInt32BE = function(value, offset = 0) {
                    for (let i = 0; i < 4; i++) {
                        if (offset + i < bytes.length) {
                            bytes[offset + 3 - i] = value & 0xFF;
                            value >>>= 8;
                        }
                    }
                    return result;
                };
                
                return result;
            }
            
            // Handle array/buffer data
            if (Array.isArray(data) || ArrayBuffer.isView(data)) {
                return {
                    data: data,
                    length: data.length,
                    toString: function() {
                        return Array.from(data).map(b => String.fromCharCode(b)).join('');
            if (Array.isArray(data) || ArrayBuffer.isView(data) || (data instanceof ArrayBuffer)) {
                const bytes = Array.isArray(data) ? data : new Uint8Array(data.buffer || data);
                const result = {
                    data: new Uint8Array(bytes),
                    length: bytes.length,
                    toString: function(enc) {
                        if (enc === 'hex') {
                            return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
                        }
                        return Array.from(bytes).map(b => String.fromCharCode(b)).join('');
                    }
                };
                
                // Add SRP methods
                result.slice = function(start, end) {
                    return Buffer.from(bytes.slice(start, end));
                };
                
                result.readUInt32BE = function(offset = 0) {
                    let value = 0;
                    for (let i = 0; i < 4; i++) {
                        value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
                    }
                    return value;
                };
                
            // Default fallback
            return {
                data: data || '',
                length: (data && data.length) || 0,
                toString: function() { return ''; }
                result.writeUInt32BE = function(value, offset = 0) {
                    for (let i = 0; i < 4; i++) {
                        if (offset + i < bytes.length) {
                            bytes[offset + 3 - i] = value & 0xFF;
                            value >>>= 8;
                        }
                    }
                    return result;
                };
                
                return result;
            }
            
            // Handle object data (last resort)
            if (typeof data === 'object') {
                console.warn('Buffer.from called with object type', data);
                const result = {
                    data: data,
                    length: data.length || 0,
                    toString: function() { return JSON.stringify(data); }
                };
                
                // Add SRP methods
                result.slice = function() { return Buffer.from({}); };
                result.readUInt32BE = function() { return 0; };
                result.writeUInt32BE = function() { return result; };
                
                return result;
            }
            
            // Default fallback for any other type
            console.warn('Buffer.from called with unsupported type:', typeof data);
            const result = {
                data: new Uint8Array(0),
                length: 0,
                toString: function() { return ''; },
                slice: function() { return Buffer.from([]); },
                readUInt32BE: function() { return 0; },
                writeUInt32BE: function() { return result; }
            };
            
            return result;
        },
        isBuffer: function(obj) { return obj && obj.data !== undefined; },
        alloc: function(size) {
            const arr = new Array(size).fill(0);
            return {
                data: arr,
        
        isBuffer: function(obj) { 
            return obj && (obj.data !== undefined || (originalBuffer && originalBuffer.isBuffer && originalBuffer.isBuffer(obj)));
        },
        
        alloc: function(size, fill = 0) {
            const bytes = new Array(size).fill(fill);
            const result = {
                data: new Uint8Array(bytes),
                length: size,
                toString: function() { return ''; }
                toString: function(enc) { 
                    if (enc === 'hex') {
                        return bytes.map(b => b.toString(16).padStart(2, '0')).join('');
                    }
                    return bytes.map(b => String.fromCharCode(b)).join('');
                }
            };
            
            // Add SRP methods
            result.slice = function(start, end) {
                return Buffer.from(bytes.slice(start, end));
            };
            
            result.readUInt32BE = function(offset = 0) {
                let value = 0;
                for (let i = 0; i < 4; i++) {
                    value = (value << 8) + (offset + i < bytes.length ? bytes[offset + i] : 0);
                }
                return value;
            };
            
            result.writeUInt32BE = function(value, offset = 0) {
                for (let i = 0; i < 4; i++) {
                    if (offset + i < bytes.length) {
                        bytes[offset + 3 - i] = value & 0xFF;
                        value >>>= 8;
                    }
                }
                return result;
            };
            
            return result;
        },
        
        concat: function(list) {
            // Simple implementation that handles our use case
            return {
                data: list.map(b => b.data).join(''),
                length: list.reduce((acc, b) => acc + (b.length || 0), 0),
                toString: function() { 
                    return list.map(b => b.toString()).join('');
            if (!Array.isArray(list) || list.length === 0) {
                return Buffer.alloc(0);
            }
            
            // Combine all buffers into one
            const totalLength = list.reduce((acc, buf) => acc + (buf ? (buf.length || 0) : 0), 0);
            const combinedArray = new Uint8Array(totalLength);
            
            let offset = 0;
            for (const buf of list) {
                if (buf && buf.data) {
                    const data = buf.data instanceof Uint8Array ? buf.data : new Uint8Array(buf.data);
                    combinedArray.set(data, offset);
                    offset += buf.length;
                }
            }
            
            const result = {
                data: combinedArray,
                length: totalLength,
                toString: function(enc) { 
                    if (enc === 'hex') {
                        return Array.from(combinedArray).map(b => b.toString(16).padStart(2, '0')).join('');
                    }
                    return Array.from(combinedArray).map(b => String.fromCharCode(b)).join('');
                }
            };
            
            // Add SRP methods
            result.slice = function(start, end) {
                const slicedData = combinedArray.slice(start, end);
                return Buffer.from(slicedData);
            };
            
            result.readUInt32BE = function(offset = 0) {
                let value = 0;
                for (let i = 0; i < 4; i++) {
                    value = (value << 8) + (offset + i < combinedArray.length ? combinedArray[offset + i] : 0);
                }
                return value;
            };
            
            result.writeUInt32BE = function(value, offset = 0) {
                for (let i = 0; i < 4; i++) {
                    if (offset + i < combinedArray.length) {
                        combinedArray[offset + 3 - i] = value & 0xFF;
                        value >>>= 8;
                    }
                }
                return result;
            };
            
            return result;
        }
    };
    
    // Add missing crypto methods that SRP might need
    if (window.crypto && !window.crypto.randomBytes) {
    if (window.crypto) {
        if (!window.crypto.randomBytes) {
            window.crypto.randomBytes = function(size) {
                const array = new Uint8Array(size);
                window.crypto.getRandomValues(array);
                return Buffer.from(array);
            };
        }
        
        // Add cryptographic hash functions if needed
        if (!window.crypto.createHash) {
            window.crypto.createHash = function(algorithm) {
                return {
                data: array,
                length: size,
                toString: function(encoding) {
                    update: function(data) {
                        this.data = data;
                        return this;
                    },
                    digest: async function(encoding) {
                        // Use the SubtleCrypto API for actual hashing
                        const dataBuffer = typeof this.data === 'string' ? 
                            new TextEncoder().encode(this.data) : 
                            this.data;
                        
                        let hashBuffer;
                        try {
                            if (algorithm === 'sha256') {
                                hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer);
                            } else if (algorithm === 'sha1') {
                                hashBuffer = await window.crypto.subtle.digest('SHA-1', dataBuffer);
                            } else {
                                console.error('Unsupported hash algorithm:', algorithm);
                                return Buffer.alloc(32); // Return empty buffer as fallback
                            }
                            
                            const hashArray = Array.from(new Uint8Array(hashBuffer));
                            
                            if (encoding === 'hex') {
                        return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join('');
                                return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
                            }
                            
                            return Buffer.from(hashArray);
                        } catch (e) {
                            console.error('Hash calculation failed:', e);
                            return Buffer.alloc(32); // Return empty buffer as fallback
                        }
                    return Array.from(array).map(b => String.fromCharCode(b)).join('');
                    }
                };
            };
        }
    }
    
    // Patch the SRP implementation for browser compatibility
    if (window.process) {
    if (!window.process) {
        window.process = {
            env: {
                NODE_ENV: 'production'
            }
        };
    }
    
    // Add any missing process methods
    window.process.nextTick = window.process.nextTick || function(fn) { 
        setTimeout(fn, 0); 
    };
    }
    
    console.log('Ente URL and SRP patches applied successfully');
})();
EOF
ENDPATCHES

# Create the static HTML files with scripts pre-injected
for app_dir in photos accounts auth cast; do