-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding FCLuaScriptItem
and FCLuaScriptItems
to RGP Lua and JetStream Controller
#287
Comments
This issue will probably answer your question of how the JetStream currently works in case you don't want to dig through all the source code: CJGarciaMusic/PowerUserWorkflow#11 In short, there isn't any require statements at the core of the JetStream. Just a big switch statement (built with if-thens) to execute the right function. |
I have uploaded tentative documentation to the master branch of the PDK Framework Docs Repo. To review it:
Feedback is much appreciated before I get too much further into it. |
On further reflection, I realize this does not address the fundamental challenge for JetStream Controller: that of passing a trigger value from the controller to the Lua script. But maybe this will be useful in a future refactoring. |
Ok, just to make sure I understand the docs, if I were to want to run a script I could do something like this? local script_to_execute = "My script's name"
local script_items = finenv.CreateLuaScriptItems()
for item in each(script_items) do
if item.GetMenuItemText() == script_to_execute then
finenv.ExecuteLuaScriptItem(item.GetExecutableIndex())
break
end
end This seems like a good API to me. Couple questions/thoughts, though:
|
|
I see. I think that eases my concerns. LGTM. |
It used to be a UserValueInput box, but now it’s just a small CustomLuaWindow with an entry box.
There is now code in there that parses multiple commands, and also code that accepts multiple different input values beyond the 4-digit codes.
This lets you put in, say “p < -f” and it will execute functions to add a starting p dynamic, add in a hairpin, and add a f dynamic at the end. I’m actually going to modify this further so that it will accept “p < f” and turn the second dynamic into an “end” dynamic.
I actually LOVE the little dialog box and don’t want to lose it, but if there is a way to pass the plug-in arguments without actually opening it that really would be slick.
… On Jul 30, 2022, at 3:35 PM, Nick Mazuk ***@***.***> wrote:
Ok, just to make sure I understand the docs, if I were to want to run a script I could do something like this?
local script_to_execute = "My script's name"
local script_items = finenv.CreateLuaScriptItems()
for item in each(script_items) do
if item.GetMenuItemText() == script_to_execute then
finenv.ExecuteLuaScriptItem(item.GetExecutableIndex())
break
end
end
This seems like a good API to me. Couple questions/thoughts, though:
It looks like you can add a script using finenv.AddLuaScriptItem? I assume this would not add scripts to the menu bar and any added scripts would not persist between Finale restarts, but just want to double-check.
Likewise, I assume methods like SetMenuItemText would not update the menu bar and the changes would not persist to the next session. I also assume it wouldn't update the source file.
GetPrefix: what does this do? I can't think of what a script prefix is.
AddLuaScriptItem seems like a security issue. Usually a user needs to download the script, and then they've implicitly consented to running that script. However, I could see someone creating a malicious script that adds a new script then immediately executes it. This other Lua script doesn't even necessarily need to be a script the user downloads. Now, would I think this ever would happen? No. Would it be easier to just write the malicious Lua code directly in your script? Yes. Would it be even easier to just execute an arbitrary terminal command (which is already possible)? Yep. But still, I can't think of a reason why someone would want to add a new script that's compelling enough to add another attack vector. With that said, I'm not against this feature since in reality it doesn't let a malicious user do anything new, but I think it's something to think about.
SetFilePath this seems like a security issue. Let's say we have three scripts, A: "a.lua", B: "b.lua", and C: "c.lua". "c.lua" as some malicious code and may not even be a Finale Lua script. A uses SetFilePath to change the file of B to "c.lua". User tries to run "b.lua" by clicking on "B" and accidentally runs "c.lua". Whoops! They've now run malicious code. There are the same caveats as above, though.
—
Reply to this email directly, view it on GitHub <#287 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AMCBUQ22JJZ6FCRPR4DPV5TVWWUZTANCNFSM55DZFJMQ>.
You are receiving this because you were mentioned.
|
Actually, based on your feedback (@Nick-Mazuk ), I realized the Add and Delete APIs are probably unnecessary. All we really need is Create (collection) and Execute. The Execute method can execute one you create from scratch, which allows for on-the-fly additions. FWIW: I can't see any increased security risk from Execute over what is already there with |
With excellent feedback from this thread, I think I have zeroed in on a workable set of APIs that preserves a modicum of security. There will basically be two flavors of
Items from Finale's plugin menu will be launch-and-forget and behave exactly as if you launched them from the menu. As a security precaution, your launcher script cannot see the file paths for them. (It occurred to me that a malicious script could rewrite third-party scripts if it knew where they were. This allows you to launch them without knowing anything about them other than their menu and other descriptive text.) Furthermore, the only thing that matters for these scripts is the (read-only) "execution index". No changes you make to the script item have any effect on the launching of these scripts. (Specifically, changing the prefix has no effect.) Adhoc items will allow you to launch any file of your choosing. If it isn't proper Lua, of course, you'll just get an error. The big difference with these is your script has to stay alive as long as the launched script stays alive. For scripts with modeless dialogs and retained lua states, this is longer than just a single script invocation. Also, the properties on I've updated the docs at the doc repo. If you would like to try out a prerelease version of it, you can download it here. No guarantees on how long the link stays up. But a few days at least. The Apple version is not notarized, so the usual rigamarole will be required to bless it. |
P.S. I've also hooked up |
P.P.S. I removed the constructor on |
Awesome, thank you Robert! |
I've been thinking that a script-launching script might like to have access to some of the other Notes Any others? |
It would also be nice to also get the script's text contents, or at least the hash (SHA256?) of the contents. And the filename and path of the script (which I know I already argued against earlier). I've been trying to figure out a good way of making it easy for users to update their scripts. After thinking about it, I put together a proposal in #322, but it would need these things. I'm open to another way of creating an updater. I just haven't figured out another way, yet. |
I am fairly concerned about revealing a file path on the user's computer to a third party. A malicious third party could replace its contents, and if it were done carefully deflect the blame back to the script it modified. Is there some way we could do this online? One of the |
I've done some preliminary investigation into adding hashing to RGP Lua, and it is not completely straightforward or necessarily lightweight. Here is my proposal:
The advantages of this approach are:
Disadvantages are:
|
I like the idea of inserting the hash into the plugindef at bundle time. Definitely less error prone.
+1 to this. My follow-up question would be then how to actually update the script. My proposal in #322 would require the script to get the location of the outdated script in order to automatically update it. A stop-gap would be to just direct users to the website to download it themselves. While this would work, in my opinion updating scripts should be as painless and simple as possible for end users. I want to try and keep as many people as possible on the latest versions of everything. An alternative is we could add a new method to RGP Lua that allows other scripts to update a script file. Script A will call this API to update script B and will tell RGP Lua B's new contents. RGP Lua will then ask the user whether script A should be allowed to update script B. If the user accepts, RGP Lua will overwrite script B's code with the new contents and return Lastly, since a user will likely run the updater many times, and each time many scripts will be updated, it would be nice for allow the user to "trust" the updater script. And in the future, RGP Lua will not prompt for the user's consent in the future. All this would have to be done inside the RGP Lua plugin. Thoughts? |
On another note, @ThistleSifter suggested in #145 of letting a script report a success/error case. I think that's a decent proposal. Perhaps the Also clarification, does |
Answers in no particular order:
|
I see, thanks for the clarification!
I think simply returning nil on success and a string on error is sufficient. Though I'm curious on @ThistleSifter's thoughts. However, the next question is what that string should be. For instance, if a script puts One alternative is status codes and a message. One popular implementation of this pattern is Of course, we may still wish to have a custom message, so perhaps a tuple of strings? -- structure
return "status code", "message"
-- default if the return statement isn't included
return "ok", "" Then from anywhere in the script you can return "ok" or an error. We can easily abstract the details of this into a library function to ensure consistency across this repo. -- status creation
status.ok() -- "ok", ""
status.ok("my message") -- "ok", "my message"
-- status parsing option 1
local status_code, message = invoke_script()
local is_ok = status.is_ok(status_code)
-- status parsing option 2 (which may be more forward compatible should we choose to change the format in the future)
local result = invoke_script()
local is_ok = status.is_ok(result)
local message = status.get_message(result)
Hmm… isn't this already an issue with the configuration library? How does the configuration library currently work if the script is in a protected directory? While I'm not against for an external installer, I'd prefer keeping it in Lua if possible. That would just be much simpler to maintain since we already have a pipeline for that. A fallback is that if RGP Lua or the script cannot write the file, we can just prompt the user to download the script from the website manually.
Ah true. Since the best place to store this is likely in the XML file, which is in a consistent place for every RGP Lua user.
That would be a useful function. The main security issue I can think of is that now the host of the external URL is an attack vector. If we point to URLs we control (for instance, the website), it's likely not a huge deal. However, if someone were to ever take control of the external URL, they can potentially get arbitrary code to run on a user's computer. The two main ways I can think of are the following:
However, I'm not particularly worried about these.
For saving the contents of the string to the Lua script, that's the entire point of the operator. And the user needs to consent for this to happen. If we're worried about our website becoming compromised, the source code is open-source. So should anyone add malicious code, it'll be easy for us to replace it (or to move the site to a new domain). Also, all my accounts are secured with 2FA and the DNS has DNSSEC turned on, so it should be fairly difficult for an attacker to spoof the site. If someone does do something malicious with the site, I think that means we likely have larger problems since right now, there's practically no incentive to. If the script points to a third-party site, I think one could make the case that the user assumed that risk at the time of downloading the script. I think the largest consideration is that any HTTP call should be secured with HTTPS (this also goes for the images in #327). HTTP is very, very insecure. If we were really concerned about the URL host being compromised, we could also have this be another popup generated by RGP Lua, saying "the script wants to access this URL", and potentially letting the script give some sort of reasoning like ("so we can check to see which scripts are out of date"). However, one could make the case that we'd need to do this for images as well, which could get very unwieldy very fast. |
I don't see any way in the APIs to restrict downloads to HTTPS. I could force all URL strings to start with Jan Angermüller updates his code with an external script. This strikes me as a much more reliable update process than building it into RGP Lua. Writing the configs is not a permission problem because they are specifically being written to a location designated for writing 3rd party configuration parameters. But a lot of people are putting their Lua scripts in the Finale plugin directory, and this is definitely a protected location on both Mac and Win. I'm thinking |
Just following up, I figured out how to get Windows to refuse I have also figured out how to return multiple values to Lua from C++, so Because the read-from-url function is a synchronous call, it is not going to be appropriate to download more than a few 10s of kilobytes with it. |
I'm beginning work on finishing this up. The |
I've been working on an idea that @Nick-Mazuk requested to be able to execute scripts from a different U.I. designed in Lua itself. To that we added the capability to add scripts on the fly. Today I suddenly realized this is going to address a JetStream Controller requirement as well.
Right now the program pops up an annoying dialog and enters a number which then executes a script. Well, with this new feature (coming in 0.64), your script will be able to fish its own item out of the list, modify the prefix on the fly, then re-execute itself with a different prefix. The details are a little more complicated than that, but I think it will work.
Sound interesting? If so, I'd be interested to know a little more detail on the design of the JetStream Controller now. The sequence is
@CJGarciaMusic @jwink75
The text was updated successfully, but these errors were encountered: