Attacker can freeze profit withdrawals from V3 vaults
Users of Ninja can use Vault's `withdrawProfit()` to withdraw profits. It starts with the following check:\\n```\\n   if (block.timestamp <= lastProfitTime) {\\n      revert NYProfitTakingVault__ProfitTimeOutOfBounds();\\n      }\\n```\\n\\nIf attacker can front-run user's `withdrawProfit()` TX and set lastProfitTime to block.timestamp, they would effectively freeze the user's yield. That is indeed possible using the Vault paired strategy's `harvest()` function. It is permissionless and calls `_harvestCore()`. The attack path is shown in bold.\\n```\\n      function harvest() external override whenNotPaused returns (uint256 callerFee) {\\n            require(lastHarvestTimestamp != block.timestamp);\\n                uint256 harvestSeconds = lastHarvestTimestamp > 0 ? block.timestamp \\n            - lastHarvestTimestamp : 0;\\n      lastHarvestTimestamp = block.timestamp;\\n                uint256 sentToVault;\\n          uint256 underlyingTokenCount;\\n     (callerFee, underlyingTokenCount, sentToVault) = _harvestCore();\\n            emit StrategyHarvest(msg.sender, underlyingTokenCount, \\n                   harvestSeconds, sentToVault);\\n                }\\n```\\n\\n```\\n      function _harvestCore() internal override returns (uint256 callerFee, uint256 underlyingTokenCount,      uint256 sentToVault)\\n            {\\n        IMasterChef(SPOOKY_SWAP_FARM_V2).deposit(POOL_ID, 0);\\n            _swapFarmEmissionTokens();\\n                callerFee = _chargeFees();\\n                    underlyingTokenCount = balanceOf();\\n                       sentToVault = _sendYieldToVault();\\n            } \\n```\\n\\n```\\n      function _sendYieldToVault() internal returns (uint256 sentToVault) {\\n         sentToVault = IERC20Upgradeable(USDC).balanceOf(address(this));\\n            if (sentToVault > 0) {\\n               IERC20Upgradeable(USDC).approve(vault, sentToVault);\\n            IVault(vault).depositProfitTokenForUsers(sentToVault);\\n                }\\n                  }\\n```\\n\\n```\\n      function depositProfitTokenForUsers(uint256 _amount) external nonReentrant {\\n         if (_amount == 0) {\\n            revert NYProfitTakingVault__ZeroAmount();\\n         }\\n        if (block.timestamp <= lastProfitTime) {\\n            revert NYProfitTakingVault__ProfitTimeOutOfBounds();\\n         }\\n        if (msg.sender != strategy) {\\n            revert NYProfitTakingVault__OnlyStrategy();\\n        }\\n            uint256 totalShares = totalSupply();\\n        if (totalShares == 0) {\\n            lastProfitTime = block.timestamp;\\n            return;\\n          }\\n            accProfitTokenPerShare += ((_amount * PROFIT_TOKEN_PER_SHARE_PRECISION) / totalShares);\\n               lastProfitTime = block.timestamp;\\n            // Now pull in the tokens (Should have permission)\\n            // We only want to pull the tokens with accounting\\n               profitToken.transferFrom(strategy, address(this), _amount);\\n            emit ProfitReceivedFromStrategy(_amount);\\n                }\\n```\\n
Do not prevent profit withdrawals during lastProfitTime block.
```\\n   if (block.timestamp <= lastProfitTime) {\\n      revert NYProfitTakingVault__ProfitTimeOutOfBounds();\\n      }\\n```\\n