How do I grant a capability to an account after a module has been deployed? - pact-lang

I have my contract deployed on the testnet and I am trying to call (mint-nft) which has no arguments, but it does have a require-capability ACCOUNT_GUARD. I am getting error as follows:
Error from (api.testnet.chainweb.com): : Failure: require-capability: not granted:
(free.slightly-less-shitty-nft-project.ACCOUNT_GUARD "k:ab7cef70e1d91a138b8f65d26d6917ffcb852b84525b7dc2193843e8dfebf799")
ACCOUNT_GUARD capability defined: (taken from kitty-kad contract)
(defcap ACCOUNT_GUARD(account:string) ; Used for admin functions
#doc "Verifies account meets format and belongs to caller"
(enforce (= "k:" (take 2 account)) "For security, only support k: accounts")
(enforce-guard
(at "guard" (coin.details account))
)
)
A simplified mint-nft is defined as follows:
(defun mint-nft()
#doc "Allows any k: address to mint an nft"
(require-capability (ACCOUNT_GUARD (at "sender" (chain-data))))
(insert nft-main-table id {
"id":id,
"generation":0,
"owner-address":(at "sender" (chain-data))
})
)
From my understanding, ACCOUNT_GUARD is simply checking to see that the account calling the contract is a valid k: address and that the account calling it is the owner of said account.
Why would the ACCOUNT_GUARD not be granted to the caller of (mint-nft) in this case?
How do I grant the ACCOUNT_GUARD capability to any user that calls (mint-nft)?

Capabilities must be acquired before require-capability will succeed. See https://pact-language.readthedocs.io/en/stable/pact-reference.html#expressing-capabilities-in-code-defcap for discussion of this.
Capabilities are acquired with with-capability such that code within the body of the with-capability expression have the granted capability in scope. At that point you could call mint-nft, if that was a "protected function" that you did not want users to call at top level.
However if mint-nft is the intended top-level call, then use with-capability instead of require-capability, and include the insert call in the body. (Your indentation already looks like this even though there is no body in require-capability btw).
Lastly, you should not base anything on sender -- this is the gas payer only and in Kadena gas can be paid by a gas station or really anybody. Instead you should manage a user table with the associated guard, and validate the principal when creating the user. k: without any associated guard is meaningless.

Related

How can i check the module-name in `cont` type transactions for gas-station?

I have implemented a gas-station for my project.
When i tried to pay gas for a cont type txn, and it threw me Inside an exec error.
(defcap GAS_PAYER:bool
( user:string
limit:integer
price:decimal
)
(enforce (= "exec" (at "tx-type" (read-msg))) "Inside an exec")
(enforce (= 1 (length (at "exec-code" (read-msg)))) "Tx of only one pact function")
(enforce (= "(free.anedak." (take 13 (at 0 (at "exec-code" (read-msg))))) "only free.anedak smart contract")
(compose-capability (ALLOW_GAS))
)
While looking at the code, I found that only exec type transactions are supported in gas-stations and the ability to check that the txn call is made only by my contract is only possible in exec type txns.
The data to check for module name and txn types are added in this block of code.
https://github.com/kadena-io/chainweb-node/blob/0b24dd35a7fda27719ea7a2ca54db2cfd1196d95/src/Chainweb/Pact/TransactionExec.hs#L876.
I don't think i can use the existing props for checking the module name in cont type txns.
https://github.com/kadena-io/chainweb-node/blob/8c32fcfff85c4e5b61a9554f0180ca6c90840e42/test/pact/continuation-gas-payer.pact
I found this one for cont type txns, but no checks on module name.
Any solutions on how i can check the module-name in cont type txns will be helpful as this will prevent my gas-station from spamming.

In Snowflake, you cannot reference a shared view from a function if you want to share that function, but you can reference a shared function?

I define a secure function in account A that properly references a secure view from account B. When I try to share this function through a data share, it fails with "SQL compilation error: A view or function being shared cannot reference objects from other databases."
However, if I create a wrapper function around the secure view in account B, and I refer to this function instead in account A, it works. The secure view could be as simple as "SELECT 1", so I don't need to care about REFERENCE_USAGE.
Is this expected behavior?
Simplified code:
create or replace secure function db_b.public.func_b()
returns number
as 'select top 1 val from db_a.public.view_a';
-- this fails with the error described
grant usage on function db_b.public.func_b() to share share_b;
But if I define in B:
create or replace secure function db_a.public.func_a()
returns number
as 'select top 1 val from db_a.public.view_a';
then I can use in A:
create or replace secure function db_b.public.func_b()
returns number
as 'select db_a.public.func_a()';
-- this works now!
grant usage on function db_b.public.func_b() to share share_b;
This doesn't work:
But this does:

How can I insert safely (sql injection) identifiers in a query using snowflake python connector?

In order to avoid SQL injection attacks I want to use bind variables / query parameters, but those doesn't seem to work for identifiers (like a table name, or a user name)
import snowflake.connector
import os
def myfunc(username):
with snowflake.connector.connect(user=os.environ['SF_USER'], password=os.environ['SF_PASSWORD'], account=os.environ['SF_ACCOUNT']) as ctx:
cs = ctx.cursor().execute('''SHOW GRANTS TO USER %s''', (username))
print(cs.rowcount)
rint(cs.fetchone())
myfunc('TEST2') # Raises a ProgrammingError: 001003 (42000): SQL compilation error: syntax error line1 at position 20 unexpected ''TEST2''
I guess in the above test TEST2 is inserted as a literal 'TEST2`` and not as an identifier"TEST2"`.
If I can't use bind variables / query parameters for this, I guess I would need to construct the string myself f'''SHOW GRANTS TO USER {username}''' but what options do I have to sanitize the input before the string interpolation?
By the way, I know that instead of SHOW GRANTS TO USER "<username>" I could use select * from "SNOWFLAKE"."ACCOUNT_USAGE"."GRANTS_TO_USERS" where deleted_on is null and grantee_name = '<username2' which avoid the problem since the in the latter query the username is used as a literal and not an identifier. But I found the SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_USERS to be lagging (does NOT show the latest changes immediately, delayed by more that 20 minutes in my case).
So what can I use to sanitize the values I'll use as identifiers to prevent SQL injection attacks?
As #SimeonPilgrim pointed out in a comment there is a function IDENTIFIER that can be used to convert a bind variable to an identifier.
So instead of ... TO USER %s use ... TO USER IDENTIFIER(%s)
import snowflake.connector
import os
def myfunc(username):
with snowflake.connector.connect(user=os.environ['SF_USER'], password=os.environ['SF_PASSWORD'], account=os.environ['SF_ACCOUNT']) as ctx:
cs = ctx.cursor().execute('''SHOW GRANTS TO USER IDENTIFIER(%s)''', (username))
print(cs.rowcount)
rint(cs.fetchone())
myfunc('TEST2') # Works!

Fail Flink Job if source/sink/operator has undefined uid or name

In my jobs I'd like every source/sink/operator should to have uid and name property defined for easier identification.
operator.process(myFunction).uid(MY_FUNCTION).name(MY_FUNCTION);
Right now I need to manually review every job to detect missing settings. How can I tell Flink to fail job if any name or uid is not defined?
Once you get a StreamExecutionEnvironment you can get the graph of the operators.
When you don't define a name Flink autogenerates one for you. In addition if you set a name, in case at least of sources or sinks, Flink adds a prefix Source: or Sink: to the name.
When you don't define a uid, the uid value in the graph at this stage is null.
Given your scenario, where the name and uid are always the same, to check all operator have been provided with the name and uid you can do the following:
getExecutionEnvironment().getStreamGraph().getStreamNodes().stream()
.filter(streamNode -> streamNode.getTransformationUID() == null ||
!streamNode.getOperatorName().contains(streamNode.getTransformationUID()))
.forEach(System.out::println);
This snippet will print all the operator that doesn't match with your rules.
This won't work in the 100% of cases, like using a uid which is a substring of the name. But you have here a general way to access to the operators information and apply the filters that fits in your case and perform your own strategy.
This snippet can de used as part of your CI or use it directly in your application.

VisualForce Controller SOQL query prematurely limited

I have a visual force page that references my controlling class 'ESWebCaseController.cls'.
The visual force page has a field called 'Company' that runs on an SF site and is accessible by anyone when the form is submitted is used in a SOQL query like:
List account_list = [SELECT Name FROM Account WHERE Name =:company];
This query returns 0 results when company = Acme, however this same query returns 1 result when done using the Eclipse IDE SF Schema. There is defnately a company named Acme in my org.
When I remove the WHERE clause in the query, 10 Accounts are returnted, even if I set LIMIT 100, only 10 accounts are returned. These 10 accounts seem to have one thing in common and that is that they reference the same parent account and/or have a specific field ID that references the parent account.
I want to know if there is anything that would cause the above query in my controlling class to be limited outside of the Query itself.
Below are the debug logs for the Query:
20:28:32.158 (158986000)|POP_TRACE_FLAGS|[163]|01p500000009goT|ESWebCaseController|APEX_CODE,FINEST;APEX_PROFILING,FINEST;CALLOUT,FINEST;DB,FINEST;SYSTEM,FINEST;VALIDATION,FINEST;VISUALFORCE,FINEST;WORKFLOW,FINEST
20:28:32.159 (159879000)|SOQL_EXECUTE_BEGIN|[163]|Aggregations:0|select Name from Account where Name = :tmpVar1
20:28:32.159 (159893000)|LIMIT_USAGE|[163]|SOQL|1|100
20:28:32.159 (159898000)|LIMIT_USAGE|[163]|AGGS|0|300
20:28:32.177 (177286000)|SOQL_EXECUTE_END|[163]|Rows:0
20:28:32.177 (177308000)|LIMIT_USAGE|[163]|SOQL_ROWS|0|50000
20:28:32.177 (177324000)|HEAP_ALLOCATE|[163]|Bytes:4
20:28:32.177 (177337000)|HEAP_ALLOCATE|[163]|Bytes:0
20:28:32.177 (177411000)|HEAP_ALLOCATE|[163]|Bytes:4
20:28:32.177 (177441000)|VARIABLE_SCOPE_BEGIN|[163]|account_list|LIST<Account>|true|false
20:28:32.177 (177488000)|VARIABLE_ASSIGNMENT|[163]|account_list|{"serId":1,"value":[]}|0x14cace14
20:28:32.177 (177504000)|STATEMENT_EXECUTE|[165]
Are you using with Sharing on your controller and do you have access to the account Acme from the user running the query?
By default classes have "with sharing" so they respect the sharing in the parent org, this means that if the user who accesses the page does not have access to the data the data will not be available for the query.
A quick and easy test for this is to get the Id of the record you need access to and log into salesforce then go to the url directly.
So if the record was a01adf20123032 (that's a made up id fyi) and your instance was na1 you'd log into salesforce and navigate to https://na1.salesforce.com/a01adf20123032 if you get insufficient privileges then the person doesn't have access.
Through the IDE you're most likely using a system administrator user with the "view all data" permission so they'll always get all data returned.

Resources