CVE-2021-21301 (Swift, published 2021-02-11T18:15Z)
Description: value: Wire is an open-source collaboration platform. In Wire for iOS (iPhone and iPad) before version 3.75 there is a vulnerability where the video capture isn't stopped in a scenario where a user first has their camera enabled and then disables it. It's a privacy issue because video is streamed to the call when the user believes it is disabled. It impacts all users in video calls. This is fixed in version 3.75.
Severity: MEDIUM (CVSS v3 base score: 4.3)
Weakness: CWE-200 \u2014 Exposure of Sensitive Information to an Unauthorized Actor
The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
Repository: https://github.com/wireapp/wire-ios
Files changed:
Wire-iOS Tests/Calling/SelfVideoPreviewViewTests.swift	
Wire-iOS.xcodeproj/project.pbxproj
Wire-iOS/Sources/UserInterface/Calling/VideoGridView/SelfVideoPreviewView.swift
Wire-iOS/Sources/UserInterface/Calling/VideoGridView/VideoGridViewController.swift
Commit message: [Fix] video capture state (#4879)

* stop capture when there is no self video stream

* update capture state when needed

* update capture state on didMoveToWindow

* add tests

Vulnerable code:
    private let previewView = AVSVideoPreview()
            startCapture()
        guard
            let selfStreamId = ZMUser.selfUser()?.selfStreamId,
            let selfStream = stream(with: selfStreamId)
        else {

Fixed code:
//
// Wire
// Copyright (C) 2021 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import Foundation
import XCTest
import avs

@testable import Wire

class MockAVSVideoPreview: AVSVideoPreview {
    var isCapturing: Bool = false
    
    override func startVideoCapture() {
        isCapturing = true
    }
    
    override func stopVideoCapture() {
        isCapturing = false
    }
}

class SelfVideoPreviewViewTests: XCTestCase {
    var sut: SelfVideoPreviewView!
    var stubProvider = VideoStreamStubProvider()
    var previewViewMock = MockAVSVideoPreview()
    
    override func setUp() {
        super.setUp()
        
        let stream = stubProvider.videoStream().stream
        sut = SelfVideoPreviewView(stream: stream, isCovered: false, shouldShowActiveSpeakerFrame: false)
        sut.previewView = previewViewMock
    }
    
    override func tearDown() {
        sut = nil
        super.tearDown()
    }
    
    func testCapturerState_ForVideoState_Started() {
        // Given
        previewViewMock.isCapturing = false

        // When
        sut.stream = stubProvider.videoStream(videoState: .started).stream

        // Then
        XCTAssertTrue(previewViewMock.isCapturing)
    }
    
    func testCapturerState_ForVideoState_Stopped() {
        // Given
        previewViewMock.isCapturing = false
        
        // When
        sut.stream = stubProvider.videoStream(videoState: .stopped).stream
        
        // Then
        XCTAssertFalse(previewViewMock.isCapturing)
    }
    
    func testCapturerState_ForVideoState_Paused() {
        // Given
        previewViewMock.isCapturing = false
        
        // When
        sut.stream = stubProvider.videoStream(videoState: .paused).stream
        
        // Then
        XCTAssertFalse(previewViewMock.isCapturing)
    }
    
    func testCapturerState_ForVideoState_BadConnection() {
        // Given
        previewViewMock.isCapturing = false
        
        // When
        sut.stream = stubProvider.videoStream(videoState: .badConnection).stream
        
        // Then
        XCTAssertFalse(previewViewMock.isCapturing)
    }
    
    func testCapturerState_ForVideoState_ScreenSharing() {
        // Given
        previewViewMock.isCapturing = false
        
        // When
        sut.stream = stubProvider.videoStream(videoState: .screenSharing).stream
        
        // Then
        XCTAssertFalse(previewViewMock.isCapturing)
    }
}
		6372A0F225C8438B000CCB2D /* SelfVideoPreviewViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6372A0F125C8438A000CCB2D /* SelfVideoPreviewViewTests.swift */; };
		6372A0F125C8438A000CCB2D /* SelfVideoPreviewViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfVideoPreviewViewTests.swift; sourceTree = "<group>"; };
				6372A0F125C8438A000CCB2D /* SelfVideoPreviewViewTests.swift */,
				6372A0F225C8438B000CCB2D /* SelfVideoPreviewViewTests.swift in Sources */,
    var previewView = AVSVideoPreview()
    override var stream: Stream {
        didSet {
            guard stream != oldValue else { return }
            updateCaptureState()
        }
    }
    
            updateCaptureState()
    private func updateCaptureState() {
        stream.videoState == .some(.started) ? startCapture() : stopCapture()
    }
    
        guard let selfStreamId = ZMUser.selfUser()?.selfStreamId else { return }

        guard let selfStream = stream(with: selfStreamId) else {
            (viewCache[selfStreamId] as? SelfVideoPreviewView)?.stopCapture()