The answer is that it depends.
The biggest risk is a XSS Injection attack.
Consider a scenario, your are evaling something that contains the name of a user. What if I was able to write some JS in place of my name. So when my name displays for other users (say in a feed), that JS code gets executed on their browser.
What I can do:
I can do an API call for stealing their cookies, messages,
localStorage data.
I can show a genuine looking popup saying your account has been
locked, and you need to pay up to unlock it.
Anything that JS can do.
eval() makes your code slow, it also makes debugging hard. Since you are basically executing a string, you won't get a stack trace.
A lot of developers consider it "evil". I however feel like it's not a binary choice.
I avoid it personally because I feel like it just adds techincal debt to the code as the codebase grows. You can always do things better without eval.
However, it all boils down to how much you trust the input to eval(). If you are not running eval() on a user input and just maybe hardcoding a string that you trust will be 100% safe, and makes your code easy, go for it. But as code base grows, you will face the tech debt.
Maybe I was unclear in the first place, but I'm try to understand exactly how the user can manipulate that data attribute and do something bad.
It depends on what you put in the data attribute, as in, if your data attribute contains something that directly or indirectly comes from a user input.
If the string inside the data-attribute comes from you, the developer. Say, it might be hardcoded in the HTML or maybe it comes from the server, and a user input plays no part in it. Then it's safe to use.
You need to ask yourself this question: Will a piece of user data or user input ever end up inside my data attribute?
If no, then you use it. If yes, don't use it.
Example
const usernameInputField = document.getElementById('username');
const renderedUsernameSpan = document.getElementById('rendered-username');
const button = document.getElementById('button');
usernameInputField.addEventListener("keyup", (e) => {
renderedUsernameSpan.dataset.username = e.target.value;
renderedUsernameSpan.innerHTML = e.target.value;
});
button.addEventListener('click', () => {
eval(renderedUsernameSpan.dataset.username);
});
<div>
<p>Type something in the username field and it will be set to <code>data-username.</code>
</p>
<hr>
<label for="username">Enter username:</label>
<input type="text" id="username">
</div>
<p>
<code>data-username value:</code>
<span id="rendered-username" data-username=""></span>
</p>
<hr>
<button id="button">Click to eval() the data-username</button>
Some possible inputs:
43
innocentUsername=1; alert('hacked');
window.location.href = 'https://stackoverflow.com'