Solution
What you need to do is to make it more efficient by making the lookahead run once at the beginning:
/^(?!white$|[\s\S]*(?:cat[\s\S]*dog|dog[\s\S]*cat))[\s\S]*$/i
See the regex demo ([\s\S] replaced with . only for demo since the input is tested line by line).
Explanation
The /^((?!(white|cat.*dog|dog.*cat))[\s\S])*$/i contains an anchored tempered greedy token from a well-known Regular expression to match line that doesn't contain a word? post.  [\s\S] matches any character (even a newline) but not in case it is a char that is the first in a sequence defined in the negative lookahead. So, the regex above matches any string but the one that contains either white, or cat followed with 0+ chars other than a newline and then dog, or vice versa, dog and then after 0+ chars other than a newline, cat.
So, what is necessary is to make sure white is tested in between anchors: ^(?!white$)[\s\S]*$ will do that check.
The rest of the alternatives still need to be checked inside, at any location within the string. So, the [\s\S]* should be put before the (?:cat[\s\S]*dog|dog[\s\S]*cat) group: [\s\S]*(?:cat[\s\S]*dog|dog[\s\S]*cat). That way, we make sure the string does not have these patterns inside. Note the .* in the lookahead only checked if the patterns were not present on the first line.
Details:
- ^- start of string
- (?!- the negative lookahead check:- 
- white$- the string can't equal- white
- |- or
- [\s\S]*(?:cat[\s\S]*dog|dog[\s\S]*cat)- 0+ any chars followed with either- catand then after any number of chars a- dogor vice versa
 
- )- end of the lookahead
- [\s\S]*- 0+ any chars
- $- end of string.